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.
6217 lines
236 KiB
6217 lines
236 KiB
<template>
|
|
<div class="mod-config">
|
|
<el-form :inline="true" :model="searchData" label-position="top" class="search-form">
|
|
<el-form-item label="BU">
|
|
<el-select v-model="searchData.buNo" filterable placeholder="请选择" style="width: 130px" @change="handleSearchBuChange">
|
|
<el-option
|
|
v-for="item in userBuList"
|
|
:key="`searchBu_${item.buNo}`"
|
|
:label="item.buDesc ? `${normalizeBuNo(item.buNo, searchData.site)}-${item.buDesc}` : normalizeBuNo(item.buNo, searchData.site)"
|
|
:value="normalizeBuNo(item.buNo, searchData.site)"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="项目编码">
|
|
<el-input v-model="searchData.projectNo" clearable placeholder="请输入项目编码" style="width: 140px" />
|
|
</el-form-item>
|
|
<el-form-item label="项目描述">
|
|
<el-input v-model="searchData.projectDesc" clearable placeholder="请输入项目描述" style="width: 160px" />
|
|
</el-form-item>
|
|
<el-form-item label="项目物料">
|
|
<el-input v-model="searchData.testPartNo" clearable placeholder="请输入项目物料" style="width: 140px" />
|
|
</el-form-item>
|
|
<el-form-item label="料号描述">
|
|
<el-input v-model="searchData.partDesc" clearable placeholder="请输入料号描述" style="width: 160px" />
|
|
</el-form-item>
|
|
<el-form-item v-if="searchData.buNo==='03-RFID'" label="分类">
|
|
<el-select v-model="searchData.dryWetType" clearable placeholder="全部" style="width: 100px">
|
|
<el-option v-for="item in dryWetTypeList" :key="`searchDryWet_${item}`" :label="item" :value="item" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="客户编码">
|
|
<el-input v-model="searchData.customerNo" clearable placeholder="请输入客户编码" style="width: 120px" />
|
|
</el-form-item>
|
|
<el-form-item label="打样单号">
|
|
<el-input v-model="searchData.proofingNo" clearable placeholder="请输入打样单号" style="width: 150px" />
|
|
</el-form-item>
|
|
<el-form-item label="项目/物料同步">
|
|
<el-select v-model="searchData.projectPartSyncFlag" clearable placeholder="全部" style="width: 120px">
|
|
<el-option label="已同步" value="Y" />
|
|
<el-option label="未同步" value="N" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="打样单同步">
|
|
<el-select v-model="searchData.proofSyncFlag" clearable placeholder="全部" style="width: 120px">
|
|
<el-option label="已同步" value="Y" />
|
|
<el-option label="未同步" value="N" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="打样状态">
|
|
<el-select v-model="searchData.proofingStatus" clearable placeholder="全部" style="width: 120px">
|
|
<el-option label="进行中" value="进行中" />
|
|
<el-option label="打样完成" value="打样完成" />
|
|
<el-option label="草稿" value="草稿" />
|
|
</el-select>
|
|
<el-button type="primary" @click="getDataList('Y')">查询</el-button>
|
|
<el-button @click="resetSearch">重置</el-button>
|
|
<el-button type="success" :loading="exportLoading" @click="exportTrackingData">导出</el-button>
|
|
</el-form-item>
|
|
<el-form-item class="button-group-col">
|
|
<el-row :gutter="8" type="flex" class="button-row" >
|
|
<el-button type="primary" @click="openOneKeyDialog">一键创建</el-button>
|
|
<!-- <el-button type="warning" @click="toggleBatchEdit">{{ batchEditMode ? '取消修改' : '批量修改' }}</el-button>
|
|
<el-button v-if="batchEditMode" type="success" @click="saveBatchPlan">保存修改</el-button>-->
|
|
<el-button type="warning" @click="openCreateProofDialog()">新增打样</el-button>
|
|
<el-button type="success" plain :loading="syncBatchNpiLoading" @click="syncBatchProjectPartToNpi()">同步项目物料到NPI</el-button>
|
|
<el-button type="success" plain :loading="syncBatchProofLoading" @click="syncBatchProofToNpi()">同步打样到NPI</el-button>
|
|
<el-button type="info" v-if="isAuth('833001005:set')" plain @click="openBuProcessConfigDialog">事业部进度设置</el-button>
|
|
<el-button type="info" v-if="isAuth('833001005:set2')" plain @click="openDefaultProcessConfigDialog">默认进度设置</el-button>
|
|
<el-tooltip style="margin-top: 2px;margin-left: 10px"
|
|
content="已完成后,双击日期选择框可回退为未完成"
|
|
placement="top">
|
|
<i class="el-icon-question default-process-tip-icon"></i>
|
|
</el-tooltip>
|
|
</el-row>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<el-table
|
|
ref="trackingTable"
|
|
:data="dataList"
|
|
border class="data-table"
|
|
stripe highlight-current-row
|
|
height="700"
|
|
v-loading="dataListLoading"
|
|
@selection-change="selectionChange"
|
|
@row-click="rowClick"
|
|
style="width: 100%; margin-top: 8px"
|
|
>
|
|
<el-table-column type="selection" width="45" />
|
|
<el-table-column
|
|
v-for="(item, trackingColumnIndex) in visibleTrackingColumns"
|
|
:key="`${item.columnProp}_${trackingColumnIndex}`"
|
|
:prop="item.columnProp"
|
|
:label="item.columnLabel"
|
|
:width="item.columnWidth"
|
|
:header-align="item.headerAlign || 'center'"
|
|
:align="item.align || 'left'"
|
|
:fixed="item.fixed === '' ? false : item.fixed"
|
|
:show-overflow-tooltip="item.showOverflowTooltip"
|
|
>
|
|
<template slot-scope="scope">
|
|
<span
|
|
v-if="isProjectPartSyncColumn(item.columnProp)"
|
|
:class="getProjectPartSyncTextClass(scope.row)"
|
|
>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span>
|
|
<span
|
|
v-else-if="item.columnProp === 'proofingNo'"
|
|
:class="getProofSyncTextClass(scope.row)"
|
|
>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span>
|
|
<a
|
|
v-else-if="item.columnProp === 'proofingStatus'"
|
|
:style="{ color: (scope.row.proofingStatus === '打样完成' || scope.row.proofingStatus === '打样结束') ? '#67c23a' : '#046e97' }"
|
|
>{{ scope.row.proofingStatus || '进行中' }}</a>
|
|
<div
|
|
v-else-if="item.columnProp === 'trackingStatus'"
|
|
:class="getTrackingStatusClass(scope.row)"
|
|
>
|
|
{{ getTrackingStatusText(scope.row) }}
|
|
</div>
|
|
<span v-else-if="item.columnProp === 'engineer'">
|
|
{{ getTrackingRoleDisplayValue(scope.row, 'engineerName', 'engineer') }}
|
|
</span>
|
|
<span v-else-if="item.columnProp === 'projectManager'">
|
|
{{ getTrackingRoleDisplayValue(scope.row, 'projectManagerName', 'projectManager') }}
|
|
</span>
|
|
<span v-else-if="item.columnProp === 'pic'">
|
|
{{ getTrackingRoleDisplayValue(scope.row, 'projectOwnerName', 'pic') }}
|
|
</span>
|
|
<span v-else>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column
|
|
v-for="(item, processColumnIndex) in visibleProcessColumns"
|
|
:key="`${item.code}_${item.sortNo || ''}_${processColumnIndex}`"
|
|
:label="item.label"
|
|
:width="getProcessColumnWidth(item)"
|
|
align="center"
|
|
header-align="center"
|
|
>
|
|
<template slot-scope="scope">
|
|
<div
|
|
v-if="isProcessColumnVisibleForRow(scope.row, item)"
|
|
:class="['process-cell', isProcessStatusComplete(scope.row, item) ? 'is-complete' : '']"
|
|
>
|
|
<el-date-picker
|
|
v-if="batchEditMode"
|
|
v-model="scope.row[item.actualField]"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
format="yyyy-MM-dd"
|
|
placeholder="实际完成日期"
|
|
style="width: 120px"
|
|
/>
|
|
<div
|
|
v-else
|
|
:class="['process-content', isProcessStatusComplete(scope.row, item) ? 'is-complete' : '']"
|
|
@mouseenter="handleProcessTooltipMouseEnter(scope.row, item, $event)"
|
|
@mousemove="handleProcessTooltipMouseMove($event)"
|
|
@mouseleave="handleProcessTooltipMouseLeave"
|
|
>
|
|
<el-date-picker
|
|
v-model="scope.row[item.actualField]"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
format="yyyy-MM-dd"
|
|
placeholder=""
|
|
style="width: 122px"
|
|
@change="handleProcessDateChange(scope.row, item, $event)"
|
|
@dblclick.native.stop="handleProcessDateDoubleClick(scope.row, item)"
|
|
/>
|
|
<el-button
|
|
v-if="!isProcessStatusComplete(scope.row, item)"
|
|
class="process-complete-btn"
|
|
type="success"
|
|
size="mini"
|
|
plain
|
|
title="标记为已完成"
|
|
@click.stop="markProcessComplete(scope.row, item)"
|
|
>✓</el-button>
|
|
</div>
|
|
</div>
|
|
<div v-else class="process-cell process-cell--masked"></div>
|
|
</template>
|
|
</el-table-column>
|
|
|
|
<el-table-column label="Comments" width="220">
|
|
<template slot-scope="scope">
|
|
<div class="comments-cell" @dblclick.stop="startCommentsEdit(scope.row)">
|
|
<template v-if="batchEditMode">
|
|
<el-input
|
|
v-model="scope.row.comments"
|
|
class="comments-edit-input"
|
|
size="mini"
|
|
clearable
|
|
placeholder="请输入Comments"
|
|
/>
|
|
</template>
|
|
<template v-else-if="isCommentsEditing(scope.row)">
|
|
<el-input
|
|
v-model="commentsDraft"
|
|
v-focus-comments-input="isCommentsEditing(scope.row)"
|
|
:ref="'commentsInput_' + scope.row.trackingId"
|
|
:id="'commentsInput_' + scope.row.trackingId"
|
|
class="comments-edit-input"
|
|
size="mini"
|
|
clearable
|
|
placeholder="请输入,回车也可保存"
|
|
@keydown.enter.native.prevent="saveCommentsEdit(scope.row)"
|
|
@keyup.esc.native="cancelCommentsEdit"
|
|
@blur="handleCommentsBlur(scope.row)"
|
|
/>
|
|
<el-button
|
|
class="comments-save-btn"
|
|
type="success"
|
|
size="mini"
|
|
plain
|
|
:loading="commentsSaveLoading"
|
|
@mousedown.native.prevent
|
|
@click.stop="saveCommentsEdit(scope.row)"
|
|
>√</el-button>
|
|
</template>
|
|
<span v-else class="comments-text" :title="scope.row.comments || ''">{{ scope.row.comments || '-' }}</span>
|
|
</div>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="操作" fixed="right" width="150" align="center">
|
|
<template slot-scope="scope">
|
|
<a @click="openOneKeyDialog('edit', scope.row)">修改</a>
|
|
<a class="action-link delete-link" @click="deleteProof(scope.row)">删除</a>
|
|
<a class="action-link end-link" v-if="scope.row.proofingNo && scope.row.proofingStatus !== '打样完成'" @click="finishProof(scope.row)">打样完成</a>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<el-pagination
|
|
@size-change="sizeChangeHandle"
|
|
@current-change="currentChangeHandle"
|
|
:current-page="pageIndex"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:page-size="pageSize"
|
|
:total="totalPage"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
style="margin-top: 12px; text-align: right"
|
|
/>
|
|
|
|
<div
|
|
v-show="processTooltip.visible"
|
|
class="process-history-tooltip"
|
|
:style="{
|
|
left: `${processTooltip.left}px`,
|
|
top: `${processTooltip.top}px`
|
|
}"
|
|
>{{ processTooltip.content }}</div>
|
|
|
|
<el-dialog
|
|
title="进度展示设置"
|
|
:visible.sync="buProcessConfigDialogVisible"
|
|
width="1180px"
|
|
:close-on-click-modal="false"
|
|
custom-class="bu-process-config-dialog"
|
|
>
|
|
<div class="bu-process-config-tip">
|
|
<i class="el-icon-info"></i>
|
|
<span>拖动可调整顺序(按序号从左到右),勾选控制可见列;工序分类按 BU 独立维护</span>
|
|
</div>
|
|
<el-table :data="buProcessConfigRows" border stripe max-height="560">
|
|
<el-table-column prop="buDesc" label="BU" width="120" show-overflow-tooltip />
|
|
<el-table-column label="可见进度列表 / 工序分类" min-width="760" class-name="bu-process-list-column">
|
|
<template slot-scope="scope">
|
|
<el-checkbox-group
|
|
v-model="scope.row.processCodes"
|
|
class="bu-process-order-list"
|
|
@change="handleBuProcessCodesChange(scope.row)"
|
|
>
|
|
<div
|
|
v-for="(processCode, processCodeIndex) in scope.row.processOrderCodes"
|
|
:key="`${scope.row.buNo}_${processCode}_${processCodeIndex}`"
|
|
:class="['bu-process-order-item', isProcessOrderDraggingItem(scope.row, processCodeIndex) ? 'is-dragging' : '']"
|
|
draggable="true"
|
|
@dragstart="handleProcessOrderDragStart(scope.row, processCodeIndex, $event)"
|
|
@dragover.prevent
|
|
@dragenter.prevent
|
|
@drop.prevent="handleProcessOrderDrop(scope.row, processCodeIndex, $event)"
|
|
@dragend="handleProcessOrderDragEnd"
|
|
>
|
|
<span class="process-order-index">{{ processCodeIndex + 1 }}.</span>
|
|
<i class="el-icon-rank process-order-handle"></i>
|
|
<el-checkbox :label="processCode">{{ getProcessLabelByCode(processCode) }}</el-checkbox>
|
|
<el-select
|
|
class="bu-process-category-select"
|
|
:value="getBuProcessCategoryCode(scope.row, processCode)"
|
|
size="mini"
|
|
clearable
|
|
filterable
|
|
placeholder="未分类"
|
|
@change="handleBuProcessCategoryChange(scope.row, processCode, $event)"
|
|
@mousedown.native.stop
|
|
@click.native.stop
|
|
>
|
|
<el-option
|
|
v-for="categoryItem in getProcessCategoryOptionsByBuNo(scope.row.buNo)"
|
|
:key="`buProcessCategory_${scope.row.buNo}_${categoryItem.categoryCode}`"
|
|
:label="`${categoryItem.categoryName}`"
|
|
:value="categoryItem.categoryCode"
|
|
/>
|
|
</el-select>
|
|
</div>
|
|
</el-checkbox-group>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="分类维护" width="100" align="center">
|
|
<template slot-scope="scope">
|
|
<a type="text" @click="openProcessCategoryDialog(scope.row)">维护分类</a>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
|
|
<div slot="footer">
|
|
<el-button @click="buProcessConfigDialogVisible = false">关闭</el-button>
|
|
<el-button type="primary" @click="saveBuProcessConfig">保存设置</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog
|
|
title="默认进度设置"
|
|
:visible.sync="defaultProcessConfigDialogVisible"
|
|
width="600px" class="zxClass"
|
|
:close-on-click-modal="false"
|
|
custom-class="default-process-config-dialog"
|
|
@opened="initDefaultProcessConfigSortable"
|
|
@closed="handleDefaultProcessConfigDialogClosed"
|
|
>
|
|
<div class="default-process-config-actions">
|
|
<el-button type="success" plain size="mini" @click="addDefaultProcessConfigRow">新增一行</el-button>
|
|
<el-button
|
|
type="danger"
|
|
plain
|
|
size="mini"
|
|
:disabled="defaultProcessConfigSelectionRows.length === 0 || defaultProcessConfigDeleteLoading"
|
|
:loading="defaultProcessConfigDeleteLoading"
|
|
@click="deleteDefaultProcessConfigRow"
|
|
>删除选中</el-button>
|
|
</div>
|
|
<el-table
|
|
ref="defaultProcessConfigTable"
|
|
:data="defaultProcessConfigRows"
|
|
row-key="_rowKey"
|
|
border
|
|
stripe
|
|
max-height="640"
|
|
@selection-change="handleDefaultProcessConfigSelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="50" />
|
|
<el-table-column label="排序" width="90" align="center">
|
|
<template slot-scope="scope">
|
|
<i class="el-icon-rank default-process-drag-handle"></i>
|
|
<span class="default-process-order-index">{{ scope.$index + 1 }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="工序编码" min-width="160">
|
|
<template slot-scope="scope">
|
|
<el-input v-model.trim="scope.row.code" size="mini" clearable placeholder="如: coating" />
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="工序名称" min-width="180">
|
|
<template slot-scope="scope">
|
|
<el-input v-model.trim="scope.row.label" size="mini" clearable placeholder="如: Coating" />
|
|
</template>
|
|
</el-table-column>
|
|
<!-- <el-table-column label="启用" width="100" align="center">
|
|
<template slot-scope="scope">
|
|
<el-select v-model="scope.row.activeFlag" size="mini" style="width: 80px;">
|
|
<el-option label="是" value="Y" />
|
|
<el-option label="否" value="N" />
|
|
</el-select>
|
|
</template>
|
|
</el-table-column>-->
|
|
</el-table>
|
|
<div v-if="defaultProcessConfigRows.length === 0" class="default-process-config-empty">
|
|
当前没有默认行,请点击“新增一行”后填写并保存。
|
|
</div>
|
|
|
|
<div slot="footer">
|
|
<el-button @click="defaultProcessConfigDialogVisible = false">关闭</el-button>
|
|
<el-button type="primary" @click="saveDefaultProcessConfig">保存设置</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog
|
|
:title="`工序分类维护(${processCategoryDialogBuDesc || processCategoryDialogBuNo || '-'})`"
|
|
:visible.sync="processCategoryDialogVisible"
|
|
width="450px"
|
|
:close-on-click-modal="false"
|
|
custom-class="process-category-config-dialog"
|
|
@closed="handleProcessCategoryDialogClosed"
|
|
>
|
|
<div class="default-process-config-actions">
|
|
<el-button type="success" plain size="mini" @click="addProcessCategoryRow">新增一行</el-button>
|
|
<el-button
|
|
type="danger"
|
|
plain
|
|
size="mini"
|
|
:disabled="processCategorySelectionRows.length === 0 || processCategorySaveLoading"
|
|
:loading="processCategorySaveLoading"
|
|
@click="deleteProcessCategoryRows"
|
|
>删除选中</el-button>
|
|
</div>
|
|
<el-table
|
|
ref="processCategoryTable"
|
|
:data="processCategoryRows"
|
|
row-key="_rowKey"
|
|
border
|
|
stripe class="zxClass"
|
|
max-height="520"
|
|
@selection-change="handleProcessCategorySelectionChange"
|
|
>
|
|
<el-table-column type="selection" width="50" />
|
|
<el-table-column label="分类名称" min-width="280">
|
|
<template slot-scope="scope">
|
|
<el-input v-model.trim="scope.row.categoryName" size="mini" clearable placeholder="如: 制前工序" />
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<div v-if="processCategoryRows.length === 0" class="default-process-config-empty">
|
|
当前没有分类,请点击“新增一行”后填写并保存。
|
|
</div>
|
|
<div slot="footer">
|
|
<el-button @click="processCategoryDialogVisible = false">关闭</el-button>
|
|
<el-button type="primary" :loading="processCategorySaveLoading" @click="saveProcessCategoryConfig">保存分类</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog :title="oneKeyDialogMode === 'edit' ? '修改项目/物料/打样' : '一键创建项目/物料/打样'" :visible.sync="oneKeyDialogVisible" :close-on-click-modal="false" width="1000px">
|
|
<el-form :model="oneKeyForm" label-position="top" class="one-key-grid-form">
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('buNo')">
|
|
<el-form-item label="BU" required>
|
|
<el-select v-model="oneKeyForm.buNo" placeholder="请选择" style="width: 100%" @change="handleOneKeyBuChange">
|
|
<el-option
|
|
v-for="item in userBuList"
|
|
:key="item.buNo"
|
|
:label="item.buDesc ? `${normalizeBuNo(item.buNo, searchData.site)}-${item.buDesc}` : normalizeBuNo(item.buNo, searchData.site)"
|
|
:value="item.buNo">
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="9" v-show="isOneKeyCreateFieldVisible('projectNo')">
|
|
<el-form-item label="项目编码">
|
|
<el-autocomplete
|
|
v-model.trim="oneKeyForm.projectNo"
|
|
:fetch-suggestions="queryOneKeyProjectSuggestions"
|
|
:trigger-on-focus="true"
|
|
placeholder="手工输入或按BU选择NPI项目"
|
|
style="width: 100%"
|
|
@select="handleOneKeyProjectSelect"
|
|
@blur="handleOneKeyProjectNoBlur">
|
|
<template slot-scope="{ item }">
|
|
<div class="one-key-suggest-main">{{ item.value }}</div>
|
|
<div class="one-key-suggest-sub">
|
|
{{ item.projectDesc || '-' }} | {{ item.customerDesc || '-' }}
|
|
</div>
|
|
</template>
|
|
</el-autocomplete>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="9" v-show="isOneKeyCreateFieldVisible('projectDesc')">
|
|
<el-form-item label="项目名称">
|
|
<el-input v-model="oneKeyForm.projectDesc" style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('customerNo')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label">
|
|
<a href="javascript:void(0)" @click="getBaseList(509)">客户编码</a>
|
|
<a href="javascript:void(0)" @click="newCustomer">(新客户)</a>
|
|
</span>
|
|
<el-input v-model="oneKeyForm.customerNo" @blur="customerNoBlur" style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12" v-show="isOneKeyCreateFieldVisible('customerDesc')">
|
|
<el-form-item label="客户名称">
|
|
<el-input v-model="oneKeyForm.customerDesc" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cProjectRegion')">
|
|
<el-form-item label="区域">
|
|
<el-select v-model="oneKeyForm.cProjectRegion" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option
|
|
v-for="i in cProjectRegionList"
|
|
:key="i.cProjectRegion"
|
|
:label="i.cProjectRegion"
|
|
:value="i.cProjectRegion">
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectCreationDate')">
|
|
<el-form-item label="立项日期">
|
|
<el-date-picker
|
|
v-model="oneKeyForm.projectCreationDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('needDate')">
|
|
<el-form-item label="预计完成日期">
|
|
<el-date-picker
|
|
v-model="oneKeyForm.needDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectCategory')">
|
|
<el-form-item label="项目分类">
|
|
<el-select v-model="oneKeyForm.projectCategory" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option v-for="item in projectCategoryList" :key="item" :label="item" :value="item"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('priorityLevel')">
|
|
<el-form-item label="优先级">
|
|
<el-select v-model="oneKeyForm.priorityLevel" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option v-for="item in priorityList" :key="item" :label="item" :value="item"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('testPartNo')">
|
|
<el-form-item label="项目料号">
|
|
<el-autocomplete
|
|
v-model.trim="oneKeyForm.testPartNo"
|
|
:fetch-suggestions="queryOneKeyPartSuggestions"
|
|
:trigger-on-focus="true"
|
|
placeholder="手工输入或按项目编码选择料号"
|
|
style="width: 100%"
|
|
@select="handleOneKeyPartSelect"
|
|
@blur="handleOneKeyPartNoBlur">
|
|
<template slot-scope="{ item }">
|
|
<div class="one-key-suggest-main">{{ item.value }}</div>
|
|
<div class="one-key-suggest-sub">{{ item.partDesc || '-' }} | 创建: {{ item.createDateText || '-' }}</div>
|
|
</template>
|
|
</el-autocomplete>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12" v-show="isOneKeyCreateFieldVisible('partDesc')">
|
|
<el-form-item label="料号描述" required>
|
|
<el-input v-model="oneKeyForm.partDesc" style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="isOneKeyDryWetTypeVisible()?3:6" v-show="isOneKeyCreateFieldVisible('partType')">
|
|
<el-form-item label="料号状态">
|
|
<el-select v-model="oneKeyForm.partType" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option v-for="item in partTypeList" :key="item" :label="item" :value="item"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="3" v-show="isOneKeyCreateFieldVisible('dryWetType') && isOneKeyDryWetTypeVisible()">
|
|
<el-form-item label="分类">
|
|
<el-select v-model="oneKeyForm.dry_wet_type" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option v-for="item in dryWetTypeList" :key="item" :label="item" :value="item"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectManager')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('projectManager')">PM/Sales</a></span>
|
|
<el-input v-model="oneKeyForm.projectManagerName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectOwner')">
|
|
<el-form-item required>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('projectOwner')">PjM</a></span>
|
|
<el-input v-model="oneKeyForm.projectOwnerName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('engineer')">
|
|
<el-form-item required>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('engineer')">Engineer</a></span>
|
|
<el-input v-model="oneKeyForm.engineerName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cManufactureEngineer')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cManufactureEngineer')">MFG</a></span>
|
|
<el-input v-model="oneKeyForm.cManufactureEngineerName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer6')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer6')">IQC</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer6Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer1')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer1')">IPQC-Lam/Pri/Etch/Slit</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer1Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer2')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer2')">IPQC-Converting</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer2Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer4')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer4')">SQE</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer4Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer3')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer3')">FQC1</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer3Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer5')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer5')">FQC2</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer5Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('ipqcHardTag')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('ipqcHardTag')">IPQC-Hardtag</a></span>
|
|
<el-input v-model="oneKeyForm.ipqcHardTagName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('cQualityEngineer7')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('cQualityEngineer7')">前道工程师</a></span>
|
|
<el-input v-model="oneKeyForm.cQualityEngineer7Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('docEngineer')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('docEngineer')">文档工程师</a></span>
|
|
<el-input v-model="oneKeyForm.docEngineerName" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('docEngineer2')">
|
|
<el-form-item>
|
|
<span slot="label" class="big-label"><a href="javascript:void(0)" @click="openRoleDialog('docEngineer2')">文档工程师2</a></span>
|
|
<el-input v-model="oneKeyForm.docEngineer2Name" disabled style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectStatus')">
|
|
<el-form-item label="项目状态">
|
|
<el-select v-model="oneKeyForm.projectStatus" clearable style="width: 100%">
|
|
<el-option v-for="item in projectStatusList" :key="item" :label="item" :value="item" />
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('projectPhase')">
|
|
<el-form-item label="项目阶段">
|
|
<el-select v-model="oneKeyForm.projectPhase" clearable style="width: 100%">
|
|
<el-option
|
|
v-for="item in proofProjectPhaseList"
|
|
:key="item.projectPhase"
|
|
:label="item.projectPhase"
|
|
:value="item.projectPhase"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('proofingNo')">
|
|
<el-form-item label="打样单号">
|
|
<el-select
|
|
v-model="oneKeyForm.proofingNo"
|
|
placeholder="请选择打样单号"
|
|
clearable
|
|
filterable
|
|
:loading="oneKeyProofingApplyLoading"
|
|
:disabled="isBlankValue(oneKeyForm.projectNo)"
|
|
style="width: 100%"
|
|
@change="handleOneKeyProofingNoChange">
|
|
<el-option
|
|
v-for="item in oneKeyProofingApplyOptions"
|
|
:key="item.applyNo"
|
|
:label="item.applyNo"
|
|
:value="item.applyNo"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('proofingStatus')">
|
|
<el-form-item label="打样状态">
|
|
<el-select v-model="oneKeyForm.proofingStatus" clearable style="width: 100%">
|
|
<el-option label="草稿" value="草稿" />
|
|
<el-option label="进行中" value="进行中" />
|
|
<el-option label="打样完成" value="打样完成" />
|
|
<el-option label="打样结束" value="打样结束" />
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('requiredDeliveryDate')">
|
|
<el-form-item label="预计完成日期">
|
|
<el-date-picker
|
|
v-model="oneKeyForm.requiredDeliveryDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%"
|
|
@change="handleOneKeyRequiredDeliveryDateChange">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('planStartDate')">
|
|
<el-form-item label="打样开始日期">
|
|
<el-date-picker
|
|
v-model="oneKeyForm.planStartDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('proofingNumber')">
|
|
<el-form-item label="数量">
|
|
<el-input v-model.number="oneKeyForm.proofingNumber" style="width: 100%"></el-input>
|
|
<div v-if="oneKeySelectedApplyOption" class="apply-qty-reference">
|
|
试验申请数量:{{ getApplyQuantityDisplayText(oneKeySelectedApplyOption.applyQuantity) }}
|
|
</div>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('baseline')">
|
|
<el-form-item label="Baseline">
|
|
<el-date-picker
|
|
v-model="oneKeyForm.baseline"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="(isOneKeyCreateFieldVisible('requiredDeliveryDate') ? 18 : 24) - (isOneKeyCreateFieldVisible('baseline') ? 6 : 0)" v-show="isOneKeyCreateFieldVisible('remark')">
|
|
<el-form-item label="备注">
|
|
<el-input v-model="oneKeyForm.remark" style="width: 100%"></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
</el-form>
|
|
<el-footer style="height: 40px; margin-top: 40px; text-align: center">
|
|
<el-button @click="oneKeyDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" :loading="saveOneKeyLoading" @click="submitOneKey">{{ oneKeyDialogMode === 'edit' ? '保存修改' : '保存' }}</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
|
|
<el-dialog title="新增打样" :visible.sync="proofDialogVisible" width="768px" :close-on-click-modal="false">
|
|
<el-form :model="proofDialogData" label-position="top" class="proof-grid-form">
|
|
<el-row :gutter="16">
|
|
<el-col :span="6">
|
|
<el-form-item label="项目编码">
|
|
<el-input v-model="proofDialogData.projectNo" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="项目名称">
|
|
<el-input v-model="proofDialogData.projectDesc" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="BU">
|
|
<el-input v-model="proofDialogData.buNo" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6">
|
|
<el-form-item label="客户编码">
|
|
<el-input v-model="proofDialogData.customerNo" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="客户名称">
|
|
<el-input v-model="proofDialogData.customerDesc" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="项目料号">
|
|
<el-input v-model="proofDialogData.testPartNo" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="12">
|
|
<el-form-item label="料号描述">
|
|
<el-input v-model="proofDialogData.partDesc" disabled style="width: 100%" />
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="项目分类" required>
|
|
<el-select v-model="proofDialogData.cProjectTypeDb" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option
|
|
v-for="item in cProjectTypeDbList"
|
|
:key="item.cProjectTypeDb"
|
|
:label="item.cProjectTypeDb"
|
|
:value="item.cProjectTypeDb">
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="项目阶段" required>
|
|
<el-select v-model="proofDialogData.projectPhase" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option
|
|
v-for="item in proofProjectPhaseList"
|
|
:key="item.projectPhase"
|
|
:label="item.projectPhase"
|
|
:value="item.projectPhase">
|
|
</el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6">
|
|
<el-form-item label="打样单号" required>
|
|
<el-select
|
|
v-model="proofDialogData.proofingNo"
|
|
placeholder="请选择试验单号"
|
|
clearable
|
|
filterable
|
|
:loading="proofDialogApplyLoading"
|
|
:disabled="isBlankValue(proofDialogData.projectNo)"
|
|
style="width: 100%"
|
|
@change="handleProofDialogProofingNoChange">
|
|
<el-option
|
|
v-for="item in proofDialogApplyOptions"
|
|
:key="item.applyNo"
|
|
:label="item.applyNo"
|
|
:value="item.applyNo"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="打样开始日期" required>
|
|
<el-date-picker
|
|
v-model="proofDialogData.planStartDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="打样状态">
|
|
<el-select v-model="proofDialogData.proofingStatus" placeholder="请选择" clearable style="width: 100%">
|
|
<el-option label="草稿" value="草稿"></el-option>
|
|
<el-option label="进行中" value="进行中"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="实际完成日期">
|
|
<el-date-picker
|
|
v-model="proofDialogData.actualityDeliveryDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
|
|
<el-row :gutter="16">
|
|
<el-col :span="6">
|
|
<el-form-item label="数量" required>
|
|
<el-input v-model="proofDialogData.proofingNumber" style="width: 100%" />
|
|
<div v-if="proofDialogSelectedApplyOption" class="apply-qty-reference">
|
|
试验申请数量:{{ getApplyQuantityDisplayText(proofDialogSelectedApplyOption.applyQuantity) }}
|
|
</div>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="6">
|
|
<el-form-item label="预计完成日期" required>
|
|
<el-date-picker
|
|
v-model="proofDialogData.requiredDeliveryDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%">
|
|
</el-date-picker>
|
|
</el-form-item>
|
|
</el-col>
|
|
<el-col :span="12">
|
|
<el-form-item label="备注">
|
|
<el-input v-model="proofDialogData.remark" style="width: 100%" ></el-input>
|
|
</el-form-item>
|
|
</el-col>
|
|
</el-row>
|
|
</el-form>
|
|
|
|
<el-footer style="height: 40px; margin-top: 40px; text-align: center">
|
|
<el-button @click="proofDialogVisible = false">取 消</el-button>
|
|
<el-button type="primary" :loading="proofSaveLoading" @click="saveProofRecord">保 存</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
|
|
<el-dialog title="更新工序状态" :visible.sync="processDialogVisible" width="620px" :close-on-click-modal="false">
|
|
<el-form :model="processForm" label-position="top" label-width="120px" :inline="true">
|
|
<el-form-item label="工序">
|
|
<el-input v-model="processForm.processName" disabled style="width: 180px" />
|
|
</el-form-item>
|
|
<el-form-item label="状态" required>
|
|
<el-select v-model="processForm.status" style="width: 180px">
|
|
<el-option label="未完成" value="未完成" />
|
|
<el-option label="进行中" value="进行中" />
|
|
<el-option label="已完成" value="已完成" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label="实际完成日期">
|
|
<el-date-picker v-model="processForm.actualDate" type="date" value-format="yyyy-MM-dd" format="yyyy-MM-dd" style="width: 180px" />
|
|
</el-form-item>
|
|
<el-form-item label="备注">
|
|
<el-input type="textarea" :rows="2" v-model="processForm.remark" style="width: 570px" />
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<el-table :data="processHistoryRows" border stripe height="220" style="margin-top: 32px">
|
|
<el-table-column prop="changedDate" label="修改时间" width="120" />
|
|
<el-table-column prop="changedBy" label="修改人" width="70" />
|
|
<el-table-column prop="oldStatus" label="原状态" width="60" />
|
|
<el-table-column prop="newStatus" label="新状态" width="60" />
|
|
<el-table-column prop="oldActualDate" label="原实际" width="90" />
|
|
<el-table-column prop="newActualDate" label="新实际" width="90" />
|
|
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
|
|
</el-table>
|
|
|
|
<div slot="footer">
|
|
<el-button @click="processDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" :loading="saveProcessLoading" @click="submitProcessUpdate">保存</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog
|
|
title="打样完成"
|
|
:visible.sync="finishDialogVisible"
|
|
width="260px"
|
|
:close-on-click-modal="false"
|
|
custom-class="finish-proof-dialog">
|
|
<div class="finish-dialog-tip">
|
|
<i class="el-icon-info"></i>
|
|
<span>确认后将直接标记为<strong class="finish-tip-highlight">打样完成</strong></span>
|
|
</div>
|
|
<el-form :model="finishForm" label-width="100px" class="finish-form">
|
|
<el-form-item label="实际完成日期" required>
|
|
<el-date-picker
|
|
v-model="finishForm.actualityDeliveryDate"
|
|
type="date"
|
|
value-format="yyyy-MM-dd"
|
|
format="yyyy-MM-dd"
|
|
placeholder="选择日期"
|
|
style="width: 100%"
|
|
/>
|
|
</el-form-item>
|
|
</el-form>
|
|
<div slot="footer">
|
|
<el-button @click="finishDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" :loading="finishSaveLoading" @click="submitFinishProof">确认完成</el-button>
|
|
</div>
|
|
</el-dialog>
|
|
|
|
<el-dialog :title="roleDialogTitle" :visible.sync="roleDialogVisible" width="559px" :close-on-click-modal="false">
|
|
<el-form :inline="true" label-position="top" :model="roleSearchData" style="margin-left: 7px; margin-top: -5px;">
|
|
<el-form-item label="用户账号">
|
|
<el-input v-model="roleSearchData.username" clearable style="width: 110px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="用户名">
|
|
<el-input v-model="roleSearchData.userDisplay" clearable style="width: 110px"></el-input>
|
|
</el-form-item>
|
|
<el-form-item label="是否在用">
|
|
<el-select filterable v-model="roleSearchData.active" clearable style="width: 140px">
|
|
<el-option label="是" value="Y"></el-option>
|
|
<el-option label="否" value="N"></el-option>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item label=" ">
|
|
<el-button type="primary" style="padding: 3px 12px" @click="queryRoleDialogList">查询</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-table
|
|
:data="roleDialogList"
|
|
stripe
|
|
border
|
|
highlight-current-row
|
|
@row-dblclick="pickRole"
|
|
style="width: 100%">
|
|
<el-table-column prop="username" header-align="center" align="center" label="用户账号"></el-table-column>
|
|
<el-table-column prop="userDisplay" header-align="center" align="center" label="用户名"></el-table-column>
|
|
<el-table-column prop="active" header-align="center" align="center" label="是否在用"></el-table-column>
|
|
</el-table>
|
|
<el-footer style="height: 40px; margin-top: 10px; text-align: center">
|
|
<el-button @click="roleDialogVisible = false">关闭</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
|
|
<el-dialog title="新客户" :visible.sync="newCustomerFlag" width="348px" :close-on-click-modal="false" @close="closeNewCustomer">
|
|
<el-form :inline="true" label-position="top" :model="newCustomerData" style="margin-left: 7px; margin-top: -5px;">
|
|
<el-form-item label="客户名称">
|
|
<el-input v-model="newCustomerData.customerDesc" style="width: 313px"></el-input>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-footer style="height: 40px; margin-top: 10px; text-align: center">
|
|
<el-button type="primary" @click="saveNewCustomer">保存</el-button>
|
|
<el-button @click="newCustomerFlag = false">关闭</el-button>
|
|
</el-footer>
|
|
</el-dialog>
|
|
|
|
<Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import {
|
|
batchUpdateProofTrackingPlan,
|
|
createProofTrackingRecord,
|
|
deleteProofTracking,
|
|
exportProofTracking,
|
|
finishProofTracking,
|
|
oneKeyCreateProofTracking,
|
|
oneKeyUpdateProofTracking,
|
|
queryProofTrackingBuProcessConfig,
|
|
queryProofTrackingProcessColumns,
|
|
queryProofTrackingProcessCategories,
|
|
deleteProofTrackingProcessColumnsManage,
|
|
queryProofTrackingProcessColumnsManage,
|
|
queryProofTrackingProcessHistory,
|
|
saveProofTrackingBuProcessConfig,
|
|
saveProofTrackingProcessCategoriesManage,
|
|
saveProofTrackingProcessColumnsManage,
|
|
searchProjectInfoTracking,
|
|
searchProjectPartTracking,
|
|
searchProofTracking,
|
|
batchSyncProofTracking,
|
|
batchSyncProjectPartTracking,
|
|
updateProofTrackingComments,
|
|
updateProofTrackingProcess
|
|
} from '@/api/sampleTracking/sampleTracking'
|
|
import { searchExpApplyList } from '@/api/erf/erf'
|
|
import { eamProjectInfoSearch, eamProjectPartSearch, getCustomerNo, saveNewCustomer } from '@/api/eam/eamProject.js'
|
|
import { getSiteAndBuByUserName } from '@/api/eam/eam.js'
|
|
import {
|
|
searchBusinessInfo,
|
|
searchBusinessInfo1,
|
|
searchBusinessInfo2,
|
|
searchBusinessInfo3,
|
|
searchBusinessInfo4,
|
|
searchBusinessInfo5,
|
|
searchBusinessInfo6,
|
|
searchBusinessInfo7,
|
|
searchBusinessInfo8
|
|
} from '@/api/factory/site.js'
|
|
import { queryCustomerList } from '@/api/customer/customer'
|
|
import { getTableDefaultListLanguage, getTableUserListLanguage } from '@/api/table.js'
|
|
import Chooselist from '@/views/modules/common/Chooselist_eam'
|
|
import Sortable from 'sortablejs'
|
|
|
|
function focusCommentsNativeInput (el, vnode) {
|
|
const doFocus = () => {
|
|
const componentInput = vnode && vnode.componentInstance && vnode.componentInstance.$refs
|
|
? vnode.componentInstance.$refs.input
|
|
: null
|
|
const inputEl = componentInput || (el && el.querySelector ? el.querySelector('input') : null)
|
|
if (!inputEl || typeof inputEl.focus !== 'function') {
|
|
return false
|
|
}
|
|
inputEl.focus()
|
|
const textLength = inputEl.value ? String(inputEl.value).length : 0
|
|
if (typeof inputEl.setSelectionRange === 'function') {
|
|
inputEl.setSelectionRange(textLength, textLength)
|
|
}
|
|
return true
|
|
}
|
|
if (doFocus()) {
|
|
return
|
|
}
|
|
setTimeout(doFocus, 0)
|
|
setTimeout(doFocus, 50)
|
|
setTimeout(doFocus, 100)
|
|
}
|
|
|
|
const DEFAULT_PROCESS_COLUMNS = [
|
|
{ label: 'Raw Material', code: 'rawMaterial', planField: 'rawMaterialPlanDate', actualField: 'rawMaterialActualDate', statusField: 'rawMaterialStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Cylinder/Cutter', code: 'cylinderCutter', planField: 'cylinderCutterPlanDate', actualField: 'cylinderCutterActualDate', statusField: 'cylinderCutterStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Lamination', code: 'lamination', planField: 'laminationPlanDate', actualField: 'laminationActualDate', statusField: 'laminationStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Printing', code: 'printing', planField: 'printingPlanDate', actualField: 'printingActualDate', statusField: 'printingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Etching', code: 'etching', planField: 'etchingPlanDate', actualField: 'etchingActualDate', statusField: 'etchingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Slitting&Packing', code: 'slittingPacking', planField: 'slittingPackingPlanDate', actualField: 'slittingPackingActualDate', statusField: 'slittingPackingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Bonding', code: 'bonding', planField: 'bondingPlanDate', actualField: 'bondingActualDate', statusField: 'bondingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'CL60/CL822', code: 'cl60Cl822', planField: 'cl60Cl822PlanDate', actualField: 'cl60Cl822ActualDate', statusField: 'cl60Cl822Status', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Spotting', code: 'spotting', planField: 'spottingPlanDate', actualField: 'spottingActualDate', statusField: 'spottingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Encoding', code: 'encoding', planField: 'encodingPlanDate', actualField: 'encodingActualDate', statusField: 'encodingStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Die cut', code: 'dieCut', planField: 'dieCutPlanDate', actualField: 'dieCutActualDate', statusField: 'dieCutStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Inspection', code: 'inspection', planField: 'inspectionPlanDate', actualField: 'inspectionActualDate', statusField: 'inspectionStatus', processCategoryCode: '', processCategoryName: '' },
|
|
{ label: 'Delivery&Package', code: 'deliveryPackage', planField: 'deliveryPackagePlanDate', actualField: 'deliveryPackageActualDate', statusField: 'deliveryPackageStatus', processCategoryCode: '', processCategoryName: '' }
|
|
]
|
|
|
|
function cloneDefaultProcessColumns () {
|
|
return DEFAULT_PROCESS_COLUMNS.map(item => Object.assign({}, item))
|
|
}
|
|
|
|
function buildProcessCodeLabelMapByRows (rows) {
|
|
const nextMap = {}
|
|
if (!Array.isArray(rows)) {
|
|
return nextMap
|
|
}
|
|
rows.forEach(item => {
|
|
if (!item) {
|
|
return
|
|
}
|
|
const code = item.code == null ? '' : String(item.code).trim()
|
|
const label = item.label == null ? '' : String(item.label).trim()
|
|
if (!code || !label) {
|
|
return
|
|
}
|
|
// 同时缓存原始编码与小写编码,兼容历史配置大小写不一致的场景。
|
|
nextMap[code] = label
|
|
nextMap[code.toLowerCase()] = label
|
|
})
|
|
return nextMap
|
|
}
|
|
|
|
const ONE_KEY_ROLE_FIELDS = [
|
|
'projectManager',
|
|
'projectOwner',
|
|
'engineer',
|
|
'cManufactureEngineer',
|
|
'cQualityEngineer1',
|
|
'cQualityEngineer2',
|
|
'cQualityEngineer3',
|
|
'cQualityEngineer4',
|
|
'cQualityEngineer5',
|
|
'cQualityEngineer6',
|
|
'cQualityEngineer7',
|
|
'ipqcHardTag',
|
|
'docEngineer',
|
|
'docEngineer2'
|
|
]
|
|
|
|
const DEFAULT_TRACKING_COLUMNS = [
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProjectNo',
|
|
columnProp: 'projectNo',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '项目编码',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 90,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProjectDesc',
|
|
columnProp: 'projectDesc',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '项目描述',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 120,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1TestPartNo',
|
|
columnProp: 'testPartNo',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '项目物料',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 90,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1PartDesc',
|
|
columnProp: 'partDesc',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '物料描述',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: 'left',
|
|
columnWidth: 150,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1DryWetType',
|
|
columnProp: 'dryWetType',
|
|
headerAlign: 'center',
|
|
align: 'center',
|
|
columnLabel: '分类',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 70,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProjectRegion',
|
|
columnProp: 'cProjectRegion',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '区域',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 90,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1CustomerDesc',
|
|
columnProp: 'customerDesc',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '客户名称',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 130,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1Engineer',
|
|
columnProp: 'engineer',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: 'Engineer',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 120,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProjectManager',
|
|
columnProp: 'projectManager',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: 'PM/Sales',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 120,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProofingNo',
|
|
columnProp: 'proofingNo',
|
|
headerAlign: 'center',
|
|
align: 'left',
|
|
columnLabel: '打样单号',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 90,
|
|
showOverflowTooltip: true
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1ProofingStatus',
|
|
columnProp: 'proofingStatus',
|
|
headerAlign: 'center',
|
|
align: 'center',
|
|
columnLabel: '打样状态',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 80,
|
|
showOverflowTooltip: false
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1Baseline',
|
|
columnProp: 'baseline',
|
|
headerAlign: 'center',
|
|
align: 'center',
|
|
columnLabel: 'Baseline',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 100,
|
|
showOverflowTooltip: false
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1DeliveryVariance',
|
|
columnProp: 'deliveryVariance',
|
|
headerAlign: 'center',
|
|
align: 'center',
|
|
columnLabel: 'Delivery Variance',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 110,
|
|
showOverflowTooltip: false
|
|
},
|
|
{
|
|
serialNumber: 'proofTrackingTable1TrackingStatus',
|
|
columnProp: 'trackingStatus',
|
|
headerAlign: 'center',
|
|
align: 'center',
|
|
columnLabel: 'Tracking Status',
|
|
columnHidden: false,
|
|
columnImage: false,
|
|
columnSortable: false,
|
|
status: true,
|
|
fixed: '',
|
|
columnWidth: 150,
|
|
showOverflowTooltip: true
|
|
}
|
|
]
|
|
|
|
function cloneDefaultTrackingColumns () {
|
|
return DEFAULT_TRACKING_COLUMNS.map(item => Object.assign({}, item))
|
|
}
|
|
|
|
export default {
|
|
name: 'ProjectProofTracking',
|
|
directives: {
|
|
focusCommentsInput: {
|
|
inserted (el, binding, vnode) {
|
|
if (binding.value) {
|
|
focusCommentsNativeInput(el, vnode)
|
|
}
|
|
},
|
|
componentUpdated (el, binding, vnode) {
|
|
if (binding.value && !binding.oldValue) {
|
|
focusCommentsNativeInput(el, vnode)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
Chooselist
|
|
},
|
|
data () {
|
|
return {
|
|
searchData: {
|
|
site: this.$store.state.user.site,
|
|
userName: this.$store.state.user.name,
|
|
projectNo: '',
|
|
projectDesc: '',
|
|
buNo: '',
|
|
testPartNo: '',
|
|
partDesc: '',
|
|
dryWetType: '',
|
|
customerNo: '',
|
|
proofingNo: '',
|
|
projectPartSyncFlag: '',
|
|
proofSyncFlag: '',
|
|
proofingStatus: ''
|
|
},
|
|
trackingColumnList: cloneDefaultTrackingColumns(),
|
|
hasUserTrackingColumnConfig: false,
|
|
processColumns: cloneDefaultProcessColumns(),
|
|
processCodeLabelMap: buildProcessCodeLabelMapByRows(cloneDefaultProcessColumns()),
|
|
buProcessCategoryOptionsMap: {},
|
|
buProcessConfigDialogVisible: false,
|
|
buProcessConfigRows: [],
|
|
buProcessConfigMap: {},
|
|
defaultProcessConfigDialogVisible: false,
|
|
defaultProcessConfigRows: [],
|
|
defaultProcessConfigSelectionRows: [],
|
|
defaultProcessConfigDeleteLoading: false,
|
|
defaultProcessConfigRowSeed: 0,
|
|
defaultProcessConfigSortable: null,
|
|
processCategoryDialogVisible: false,
|
|
processCategoryDialogBuNo: '',
|
|
processCategoryDialogBuDesc: '',
|
|
processCategoryRows: [],
|
|
processCategorySelectionRows: [],
|
|
processCategorySaveLoading: false,
|
|
processCategoryRowSeed: 0,
|
|
processOrderDragState: {
|
|
buNo: '',
|
|
fromIndex: -1
|
|
},
|
|
projectCategoryList: ['Low Risk', 'High Risk', 'Sustaining'],
|
|
cProjectRegionList: [
|
|
{ cProjectRegion: 'Global' },
|
|
{ cProjectRegion: 'APAC' },
|
|
{ cProjectRegion: 'CHINA' },
|
|
{ cProjectRegion: 'EU' },
|
|
{ cProjectRegion: 'US' },
|
|
{ cProjectRegion: 'Mexico' },
|
|
{ cProjectRegion: 'Other' }
|
|
],
|
|
priorityList: ['Low', 'Middle', 'High'],
|
|
projectStatusList: ['草稿', '进行中', '已关闭'],
|
|
partTypeList: ['Active', 'On hold', 'Cancel', 'EOL'],
|
|
dryWetTypeList: ['Dry', 'Wet'],
|
|
partStatusList: ['草稿', '进行中', '已量产', '正式量产'],
|
|
cProjectTypeDbList: [
|
|
{ cProjectTypeDb: 'Sustaining' },
|
|
{ cProjectTypeDb: 'Low Risk' },
|
|
{ cProjectTypeDb: 'High Risk' }
|
|
],
|
|
proofProjectPhaseList: [
|
|
{ projectPhase: 'Prototype' },
|
|
{ projectPhase: 'Alpha' },
|
|
{ projectPhase: 'Beta' },
|
|
{ projectPhase: 'Pre-launch' }
|
|
],
|
|
// 一键创建字段白名单(仅 create 模式生效):后续确认要隐藏哪些字段时,只改这里即可。
|
|
oneKeyCreateVisibleFieldKeys: [
|
|
'buNo',
|
|
'projectNo',
|
|
'projectDesc',
|
|
'customerNo',
|
|
'customerDesc',
|
|
'cProjectRegion',
|
|
'projectCreationDate',
|
|
'needDate',
|
|
'projectCategory',
|
|
'priorityLevel',
|
|
'testPartNo',
|
|
'partDesc',
|
|
'partType',
|
|
'dryWetType',
|
|
'projectManager',
|
|
'projectOwner',
|
|
'engineer',
|
|
'cManufactureEngineer',
|
|
'cQualityEngineer6',
|
|
'cQualityEngineer1',
|
|
'cQualityEngineer2',
|
|
'cQualityEngineer4',
|
|
'cQualityEngineer3',
|
|
'cQualityEngineer5',
|
|
'ipqcHardTag',
|
|
'cQualityEngineer7',
|
|
'docEngineer',
|
|
'docEngineer2',
|
|
'projectStatus',
|
|
'projectPhase',
|
|
'proofingNo',
|
|
'proofingStatus',
|
|
'proofingNumber',
|
|
'baseline',
|
|
'planStartDate',
|
|
'requiredDeliveryDate',
|
|
'remark'
|
|
],
|
|
userBuList: [],
|
|
tagNo: '',
|
|
newCustomerFlag: false,
|
|
newCustomerData: {
|
|
customerDesc: ''
|
|
},
|
|
roleDialogVisible: false,
|
|
roleDialogTitle: '',
|
|
roleDialogField: '',
|
|
roleDialogApiKey: '0',
|
|
roleDialogList: [],
|
|
roleSearchData: {
|
|
site: this.$store.state.user.site,
|
|
username: '',
|
|
userDisplay: '',
|
|
active: '',
|
|
page: 1,
|
|
limit: 50
|
|
},
|
|
roleConfig: {
|
|
projectManager: { apiKey: '0', title: 'PM/Sales', nameField: 'projectManagerName' },
|
|
projectOwner: { apiKey: '1', title: 'PjM', nameField: 'projectOwnerName' },
|
|
cQualityEngineer1: { apiKey: '2', title: 'IPQC-Lam/Pri/Etch/Slit', nameField: 'cQualityEngineer1Name' },
|
|
cQualityEngineer2: { apiKey: '3', title: 'IPQC-Converting', nameField: 'cQualityEngineer2Name' },
|
|
cQualityEngineer3: { apiKey: '4', title: 'FQC1', nameField: 'cQualityEngineer3Name' },
|
|
cManufactureEngineer: { apiKey: '5', title: 'MFG', nameField: 'cManufactureEngineerName' },
|
|
engineer: { apiKey: '6', title: 'Engineer', nameField: 'engineerName' },
|
|
cQualityEngineer4: { apiKey: '7', title: 'SQE', nameField: 'cQualityEngineer4Name' },
|
|
cQualityEngineer5: { apiKey: '7', title: 'FQC2', nameField: 'cQualityEngineer5Name' },
|
|
cQualityEngineer6: { apiKey: '7', title: 'IQC', nameField: 'cQualityEngineer6Name' },
|
|
docEngineer: { apiKey: '7', title: '文档工程师', nameField: 'docEngineerName' },
|
|
ipqcHardTag: { apiKey: '7', title: 'IPQC-Hardtag', nameField: 'ipqcHardTagName' },
|
|
cQualityEngineer7: { apiKey: '7', title: '前道工程师', nameField: 'cQualityEngineer7Name' },
|
|
docEngineer2: { apiKey: '8', title: '文档工程师2', nameField: 'docEngineer2Name' }
|
|
},
|
|
dataList: [],
|
|
dataListLoading: false,
|
|
exportLoading: false,
|
|
syncBatchNpiLoading: false,
|
|
syncBatchProofLoading: false,
|
|
pageIndex: 1,
|
|
pageSize: 20,
|
|
totalPage: 0,
|
|
currentRow: null,
|
|
manualSelectedTrackingId: null,
|
|
selectionRows: [],
|
|
batchEditMode: false,
|
|
processHistoryMap: {},
|
|
processTooltipHoverKey: '',
|
|
processTooltip: {
|
|
visible: false,
|
|
content: '',
|
|
left: 0,
|
|
top: 0
|
|
},
|
|
processHistoryRows: [],
|
|
commentsEditingTrackingId: null,
|
|
commentsDraft: '',
|
|
commentsSaveLoading: false,
|
|
|
|
oneKeyDialogVisible: false,
|
|
oneKeyDialogMode: 'create',
|
|
saveOneKeyLoading: false,
|
|
oneKeyForm: {},
|
|
oneKeyProofingApplyOptions: [],
|
|
oneKeyProofingApplyLoading: false,
|
|
oneKeyProofingApplyQueryToken: 0,
|
|
oneKeyProjectQueryToken: 0,
|
|
oneKeyPartQueryToken: 0,
|
|
oneKeyProjectNoSnapshot: '',
|
|
oneKeyProjectRoleBaseline: {},
|
|
|
|
proofDialogVisible: false,
|
|
proofSaveLoading: false,
|
|
proofDialogData: this.getDefaultProofDialogData(),
|
|
proofDialogApplyOptions: [],
|
|
proofDialogApplyLoading: false,
|
|
proofDialogApplyQueryToken: 0,
|
|
|
|
processDialogVisible: false,
|
|
saveProcessLoading: false,
|
|
processForm: {},
|
|
|
|
finishDialogVisible: false,
|
|
finishSaveLoading: false,
|
|
finishForm: {
|
|
trackingId: null,
|
|
actualityDeliveryDate: ''
|
|
}
|
|
}
|
|
},
|
|
activated () {
|
|
this.loadUserBuList().then(() => {
|
|
return Promise.all([this.queryProcessColumns(), this.loadTrackingColumns()])
|
|
}).then(() => {
|
|
this.queryBuProcessConfig()
|
|
this.getDataList()
|
|
})
|
|
},
|
|
deactivated () {
|
|
this.destroyDefaultProcessConfigSortable()
|
|
},
|
|
computed: {
|
|
visibleTrackingColumns () {
|
|
const sourceColumns = this.hasUserTrackingColumnConfig
|
|
? (Array.isArray(this.trackingColumnList) ? this.trackingColumnList : [])
|
|
: ((Array.isArray(this.trackingColumnList) && this.trackingColumnList.length > 0)
|
|
? this.trackingColumnList
|
|
: this.buildDefaultTrackingColumns())
|
|
return sourceColumns.filter(item => this.isTrackingColumnVisible(item))
|
|
},
|
|
visibleProcessColumns () {
|
|
const searchBuNo = this.getSelectedSearchBuNo()
|
|
// 指定了查询BU时,整表进度列按该BU配置展示。
|
|
if (!searchBuNo) {
|
|
return this.processColumns
|
|
}
|
|
const visibleCodes = this.getProcessVisibleCodesByBuNo(searchBuNo)
|
|
if (!visibleCodes) {
|
|
return this.processColumns
|
|
}
|
|
const visibleColumns = this.processColumns.filter(item => visibleCodes.indexOf(item.code) > -1)
|
|
return visibleColumns.length > 0 ? visibleColumns : this.processColumns
|
|
},
|
|
oneKeySelectedApplyOption () {
|
|
const selectedApplyNo = this.oneKeyForm && !this.isBlankValue(this.oneKeyForm.proofingNo)
|
|
? String(this.oneKeyForm.proofingNo).trim()
|
|
: ''
|
|
if (!selectedApplyNo) {
|
|
return null
|
|
}
|
|
return this.oneKeyProofingApplyOptions.find(item => item && item.applyNo === selectedApplyNo) || null
|
|
},
|
|
proofDialogSelectedApplyOption () {
|
|
const selectedApplyNo = this.proofDialogData && !this.isBlankValue(this.proofDialogData.proofingNo)
|
|
? String(this.proofDialogData.proofingNo).trim()
|
|
: ''
|
|
if (!selectedApplyNo) {
|
|
return null
|
|
}
|
|
return this.proofDialogApplyOptions.find(item => item && item.applyNo === selectedApplyNo) || null
|
|
}
|
|
},
|
|
methods: {
|
|
getTrackingColumnFunctionId () {
|
|
return this.$route && this.$route.meta && this.$route.meta.menuId
|
|
? this.$route.meta.menuId
|
|
: ''
|
|
},
|
|
getTrackingColumnTableId () {
|
|
const functionId = this.getTrackingColumnFunctionId()
|
|
return functionId ? `${functionId}table1` : 'projectProofTrackingTable1'
|
|
},
|
|
buildDefaultTrackingColumns () {
|
|
const functionId = this.getTrackingColumnFunctionId()
|
|
const tableId = this.getTrackingColumnTableId()
|
|
const userId = this.$store.state.user.name
|
|
return cloneDefaultTrackingColumns().map(item => Object.assign({}, item, {
|
|
userId: userId,
|
|
functionId: functionId,
|
|
tableId: tableId,
|
|
tableName: '项目打样跟踪'
|
|
}))
|
|
},
|
|
normalizeTrackingColumnWidth (value, fallbackValue) {
|
|
const parsedValue = parseInt(value, 10)
|
|
if (!Number.isNaN(parsedValue) && parsedValue > 0) {
|
|
return parsedValue
|
|
}
|
|
const parsedFallback = parseInt(fallbackValue, 10)
|
|
return !Number.isNaN(parsedFallback) && parsedFallback > 0 ? parsedFallback : 120
|
|
},
|
|
normalizeTrackingColumnBoolean (value, fallbackValue) {
|
|
if (value === undefined || value === null || value === '') {
|
|
return fallbackValue
|
|
}
|
|
if (typeof value === 'boolean') {
|
|
return value
|
|
}
|
|
if (typeof value === 'number') {
|
|
return value !== 0
|
|
}
|
|
const normalizedValue = String(value).trim().toLowerCase()
|
|
if (normalizedValue === 'true' || normalizedValue === '1' || normalizedValue === 'y' || normalizedValue === 'yes') {
|
|
return true
|
|
}
|
|
if (normalizedValue === 'false' || normalizedValue === '0' || normalizedValue === 'n' || normalizedValue === 'no') {
|
|
return false
|
|
}
|
|
return fallbackValue
|
|
},
|
|
normalizeTrackingColumnItem (item, fallbackItem) {
|
|
const defaultItem = fallbackItem || {}
|
|
const mergedItem = Object.assign({}, defaultItem, item || {})
|
|
const rawColumnProp = mergedItem.columnProp == null ? '' : String(mergedItem.columnProp).trim()
|
|
const columnProp = rawColumnProp.toLowerCase() === 'pic' ? 'pic' : rawColumnProp
|
|
const normalizedLabel = mergedItem.columnLabel == null ? '' : String(mergedItem.columnLabel).trim()
|
|
return Object.assign({}, mergedItem, {
|
|
columnProp: columnProp,
|
|
columnLabel: normalizedLabel || defaultItem.columnLabel || columnProp,
|
|
headerAlign: mergedItem.headerAlign || defaultItem.headerAlign || 'center',
|
|
align: mergedItem.align || defaultItem.align || 'left',
|
|
fixed: mergedItem.fixed == null ? (defaultItem.fixed || '') : mergedItem.fixed,
|
|
columnWidth: this.normalizeTrackingColumnWidth(mergedItem.columnWidth, defaultItem.columnWidth),
|
|
showOverflowTooltip: this.normalizeTrackingColumnBoolean(mergedItem.showOverflowTooltip, defaultItem.showOverflowTooltip !== false),
|
|
status: this.normalizeTrackingColumnBoolean(mergedItem.status, true),
|
|
columnHidden: this.normalizeTrackingColumnBoolean(mergedItem.columnHidden, false)
|
|
})
|
|
},
|
|
normalizeTrackingColumns (rows, appendDefaultColumns) {
|
|
const defaultColumns = this.buildDefaultTrackingColumns()
|
|
const defaultColumnMap = {}
|
|
defaultColumns.forEach(item => {
|
|
defaultColumnMap[item.columnProp] = item
|
|
})
|
|
const mergedRows = []
|
|
const usedColumnPropMap = {}
|
|
if (Array.isArray(rows)) {
|
|
rows.forEach(item => {
|
|
const columnProp = item && item.columnProp != null ? String(item.columnProp).trim() : ''
|
|
if (!columnProp || usedColumnPropMap[columnProp]) {
|
|
return
|
|
}
|
|
usedColumnPropMap[columnProp] = true
|
|
mergedRows.push(this.normalizeTrackingColumnItem(item, defaultColumnMap[columnProp]))
|
|
})
|
|
}
|
|
if (appendDefaultColumns !== true) {
|
|
return mergedRows
|
|
}
|
|
// 用户旧配置可能缺少新加列,这里按系统默认顺序补齐,避免新增列永远看不到。
|
|
defaultColumns.forEach((defaultItem, defaultIndex) => {
|
|
if (usedColumnPropMap[defaultItem.columnProp]) {
|
|
return
|
|
}
|
|
let insertIndex = mergedRows.length
|
|
for (let i = defaultIndex + 1; i < defaultColumns.length; i++) {
|
|
const nextProp = defaultColumns[i].columnProp
|
|
const matchedIndex = mergedRows.findIndex(item => item.columnProp === nextProp)
|
|
if (matchedIndex > -1) {
|
|
insertIndex = matchedIndex
|
|
break
|
|
}
|
|
}
|
|
if (insertIndex === mergedRows.length) {
|
|
for (let i = defaultIndex - 1; i >= 0; i--) {
|
|
const prevProp = defaultColumns[i].columnProp
|
|
const matchedIndex = mergedRows.findIndex(item => item.columnProp === prevProp)
|
|
if (matchedIndex > -1) {
|
|
insertIndex = matchedIndex + 1
|
|
break
|
|
}
|
|
}
|
|
}
|
|
mergedRows.splice(insertIndex, 0, Object.assign({}, defaultItem))
|
|
usedColumnPropMap[defaultItem.columnProp] = true
|
|
})
|
|
return mergedRows
|
|
},
|
|
loadTrackingDefaultColumns (tableId) {
|
|
const queryTable = {
|
|
functionId: this.getTrackingColumnFunctionId(),
|
|
tableId: tableId,
|
|
languageCode: this.$i18n && this.$i18n.locale ? this.$i18n.locale : ''
|
|
}
|
|
return getTableDefaultListLanguage(queryTable).then(({ data }) => {
|
|
const rows = data && Array.isArray(data.rows) ? data.rows : []
|
|
this.hasUserTrackingColumnConfig = false
|
|
this.trackingColumnList = rows.length > 0
|
|
? this.normalizeTrackingColumns(rows, true)
|
|
: this.buildDefaultTrackingColumns()
|
|
this.refreshTrackingTableLayout()
|
|
return this.trackingColumnList
|
|
}).catch(() => {
|
|
this.hasUserTrackingColumnConfig = false
|
|
this.trackingColumnList = this.buildDefaultTrackingColumns()
|
|
this.refreshTrackingTableLayout()
|
|
return this.trackingColumnList
|
|
})
|
|
},
|
|
loadTrackingColumns () {
|
|
const tableId = this.getTrackingColumnTableId()
|
|
const queryTableUser = {
|
|
userId: this.$store.state.user.name,
|
|
functionId: this.getTrackingColumnFunctionId(),
|
|
tableId: tableId,
|
|
status: true,
|
|
languageCode: this.$i18n && this.$i18n.locale ? this.$i18n.locale : ''
|
|
}
|
|
return getTableUserListLanguage(queryTableUser).then(({ data }) => {
|
|
const rows = data && Array.isArray(data.rows) ? data.rows : []
|
|
if (rows.length > 0) {
|
|
this.hasUserTrackingColumnConfig = true
|
|
this.trackingColumnList = this.normalizeTrackingColumns(rows, false)
|
|
this.refreshTrackingTableLayout()
|
|
return this.trackingColumnList
|
|
}
|
|
return this.loadTrackingDefaultColumns(tableId)
|
|
}).catch(() => {
|
|
this.hasUserTrackingColumnConfig = false
|
|
this.trackingColumnList = this.buildDefaultTrackingColumns()
|
|
this.refreshTrackingTableLayout()
|
|
return this.trackingColumnList
|
|
})
|
|
},
|
|
isTrackingColumnVisible (column) {
|
|
if (!column) {
|
|
return false
|
|
}
|
|
if (this.normalizeTrackingColumnBoolean(column.status, true) === false) {
|
|
return false
|
|
}
|
|
return this.normalizeTrackingColumnBoolean(column.columnHidden, false) === false
|
|
},
|
|
isProjectPartSyncColumn (columnProp) {
|
|
return ['projectNo', 'projectDesc', 'testPartNo', 'partDesc'].indexOf(columnProp) > -1
|
|
},
|
|
getTrackingColumnDisplayValue (row, columnProp) {
|
|
if (!row || !columnProp) {
|
|
return ''
|
|
}
|
|
const value = row[columnProp]
|
|
return this.isBlankValue(value) ? '' : String(value)
|
|
},
|
|
getTrackingRoleDisplayValue (row, nameField, valueField) {
|
|
if (!row) {
|
|
return ''
|
|
}
|
|
const roleNameValue = row[nameField]
|
|
if (!this.isBlankValue(roleNameValue)) {
|
|
return String(roleNameValue)
|
|
}
|
|
const roleValue = row[valueField]
|
|
if (this.isBlankValue(roleValue)) {
|
|
return ''
|
|
}
|
|
const displayName = this.getRoleDisplayName(roleValue)
|
|
return this.isBlankValue(displayName) ? '' : displayName
|
|
},
|
|
getSearchBuOptionList () {
|
|
return this.userBuList
|
|
.map(item => this.normalizeBuNo(item && item.buNo ? item.buNo : '', this.searchData.site))
|
|
.filter(item => !!item)
|
|
},
|
|
ensureSearchBuNoSelected () {
|
|
const buOptionList = this.getSearchBuOptionList()
|
|
if (buOptionList.length === 0) {
|
|
this.searchData.buNo = ''
|
|
return ''
|
|
}
|
|
const currentBuNo = this.normalizeBuNo(this.searchData.buNo, this.searchData.site)
|
|
if (currentBuNo && buOptionList.indexOf(currentBuNo) > -1) {
|
|
this.searchData.buNo = currentBuNo
|
|
return currentBuNo
|
|
}
|
|
// 需求要求查询BU必须有值:为空或无效时默认回退到首个可用BU。
|
|
this.searchData.buNo = buOptionList[0]
|
|
return this.searchData.buNo
|
|
},
|
|
loadUserBuList () {
|
|
if (this.userBuList.length > 0) {
|
|
this.ensureSearchBuNoSelected()
|
|
this.ensureBuProcessConfigRows()
|
|
return Promise.resolve(this.userBuList)
|
|
}
|
|
const params = { username: this.$store.state.user.name }
|
|
return getSiteAndBuByUserName(params).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const excludedBuNoList = ['3_02-Hardtag', '4_04-MHM', '5_05-Alpha']
|
|
this.userBuList = (data.rows || []).filter(item => {
|
|
const buNo = item && item.buNo ? String(item.buNo).trim() : ''
|
|
return excludedBuNoList.indexOf(buNo) === -1
|
|
})
|
|
this.ensureSearchBuNoSelected()
|
|
this.ensureBuProcessConfigRows()
|
|
}
|
|
return this.userBuList
|
|
}).catch(() => {
|
|
this.userBuList = []
|
|
this.ensureSearchBuNoSelected()
|
|
this.ensureBuProcessConfigRows()
|
|
return []
|
|
})
|
|
},
|
|
queryProcessColumns () {
|
|
const selectedBuNo = this.getSelectedSearchBuNo()
|
|
const queryBuNo = selectedBuNo || '*'
|
|
// 列顺序来源规则:
|
|
// 1) 未选择查询 BU:固定按 bu='*' 读取默认工序顺序;
|
|
// 2) 已选择查询 BU:按该 BU 读取;若无配置由后端回退到 bu='*'。
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
userName: this.$store.state.user.name,
|
|
buNo: queryBuNo
|
|
}
|
|
return queryProofTrackingProcessColumns(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '加载工序列配置失败,已使用默认配置')
|
|
this.processColumns = cloneDefaultProcessColumns()
|
|
this.mergeProcessCodeLabelMapByRows(this.processColumns)
|
|
this.ensureBuProcessConfigRows()
|
|
this.refreshTrackingTableLayout()
|
|
return this.processColumns
|
|
}
|
|
const rows = Array.isArray(data.rows) ? data.rows : []
|
|
// 防御性排序:即使后端 SQL 已 ORDER BY sort_no,前端仍按 sortNo 二次排序,确保展示稳定。
|
|
const sortedRows = rows
|
|
.map((item, index) => {
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
return {
|
|
item: item,
|
|
sourceIndex: index,
|
|
sortNo: Number.isNaN(parsedSortNo) ? Number.MAX_SAFE_INTEGER : parsedSortNo
|
|
}
|
|
})
|
|
.sort((a, b) => {
|
|
if (a.sortNo !== b.sortNo) {
|
|
return a.sortNo - b.sortNo
|
|
}
|
|
return a.sourceIndex - b.sourceIndex
|
|
})
|
|
const nextColumns = []
|
|
const usedCodeMap = {}
|
|
sortedRows.forEach(({ item, sortNo }) => {
|
|
if (!item) {
|
|
return
|
|
}
|
|
const code = item.code == null ? '' : String(item.code).trim()
|
|
const label = item.label == null ? '' : String(item.label).trim()
|
|
const planField = item.planField == null ? '' : String(item.planField).trim()
|
|
const actualField = item.actualField == null ? '' : String(item.actualField).trim()
|
|
const statusField = item.statusField == null ? '' : String(item.statusField).trim()
|
|
const processCategoryCode = item.processCategoryCode == null ? '' : String(item.processCategoryCode).trim()
|
|
const processCategoryName = item.processCategoryName == null ? '' : String(item.processCategoryName).trim()
|
|
if (!code || !label || !planField || !actualField || !statusField || usedCodeMap[code]) {
|
|
return
|
|
}
|
|
usedCodeMap[code] = true
|
|
nextColumns.push({
|
|
label: label,
|
|
code: code,
|
|
planField: planField,
|
|
actualField: actualField,
|
|
statusField: statusField,
|
|
processCategoryCode: processCategoryCode,
|
|
processCategoryName: processCategoryName,
|
|
sortNo: sortNo === Number.MAX_SAFE_INTEGER ? null : sortNo
|
|
})
|
|
})
|
|
// 保障切换期可用性:当表里无有效配置时继续使用前端兜底工序定义。
|
|
this.processColumns = nextColumns.length > 0 ? nextColumns : cloneDefaultProcessColumns()
|
|
this.mergeProcessCodeLabelMapByRows(this.processColumns)
|
|
this.syncDataListProcessValues()
|
|
this.ensureBuProcessConfigRows()
|
|
this.refreshTrackingTableLayout()
|
|
return this.processColumns
|
|
}).catch(() => {
|
|
this.$message.error('加载工序列配置异常,已使用默认配置')
|
|
this.processColumns = cloneDefaultProcessColumns()
|
|
this.mergeProcessCodeLabelMapByRows(this.processColumns)
|
|
this.syncDataListProcessValues()
|
|
this.ensureBuProcessConfigRows()
|
|
this.refreshTrackingTableLayout()
|
|
return this.processColumns
|
|
})
|
|
},
|
|
getRowProcessValue (row, processCode) {
|
|
if (!row || !processCode) {
|
|
return null
|
|
}
|
|
const processMap = row.processValueMap && typeof row.processValueMap === 'object'
|
|
? row.processValueMap
|
|
: null
|
|
if (!processMap) {
|
|
return null
|
|
}
|
|
return processMap[processCode] || null
|
|
},
|
|
applyProcessValuesToRow (row) {
|
|
if (!row || !this.processColumns || this.processColumns.length === 0) {
|
|
return
|
|
}
|
|
this.processColumns.forEach(item => {
|
|
if (!item) {
|
|
return
|
|
}
|
|
const processValue = this.getRowProcessValue(row, item.code)
|
|
const planDate = processValue ? this.formatDate(processValue.planDate) : ''
|
|
const actualDate = processValue ? this.formatDate(processValue.actualDate) : ''
|
|
const status = processValue && processValue.status ? String(processValue.status).trim() : ''
|
|
this.$set(row, item.planField, planDate)
|
|
this.$set(row, item.actualField, actualDate)
|
|
this.$set(row, item.statusField, status)
|
|
})
|
|
},
|
|
syncDataListProcessValues () {
|
|
if (!Array.isArray(this.dataList) || this.dataList.length === 0) {
|
|
return
|
|
}
|
|
this.dataList.forEach(row => {
|
|
this.applyProcessValuesToRow(row)
|
|
})
|
|
},
|
|
updateRowProcessValue (row, processCol, status, actualDate) {
|
|
if (!row || !processCol) {
|
|
return
|
|
}
|
|
if (!row.processValueMap || typeof row.processValueMap !== 'object') {
|
|
this.$set(row, 'processValueMap', {})
|
|
}
|
|
const prevValue = this.getRowProcessValue(row, processCol.code) || {}
|
|
this.$set(row.processValueMap, processCol.code, Object.assign({}, prevValue, {
|
|
processCode: processCol.code,
|
|
status: status,
|
|
actualDate: actualDate || null
|
|
}))
|
|
this.$set(row, processCol.statusField, status)
|
|
this.$set(row, processCol.actualField, actualDate)
|
|
if (processCol.code === 'deliveryPackage') {
|
|
// Delivery&Package 工序日期与打样预计完成日期使用同一口径,前端先行回写避免用户感知延迟。
|
|
this.$set(row, 'requiredDeliveryDate', actualDate || '')
|
|
// baseline 仅在首次维护 Delivery&Package 日期时自动带入,后续允许独立维护。
|
|
if (!this.formatDate(row.baseline) && actualDate) {
|
|
this.$set(row, 'baseline', actualDate)
|
|
}
|
|
}
|
|
},
|
|
getAllProcessCodes () {
|
|
return this.processColumns.map(item => item.code)
|
|
},
|
|
mergeProcessCodeLabelMapByRows (rows) {
|
|
if (!Array.isArray(rows) || rows.length === 0) {
|
|
return
|
|
}
|
|
const nextMap = Object.assign({}, this.processCodeLabelMap || {})
|
|
const rowLabelMap = buildProcessCodeLabelMapByRows(rows)
|
|
Object.keys(rowLabelMap).forEach(code => {
|
|
nextMap[code] = rowLabelMap[code]
|
|
})
|
|
this.processCodeLabelMap = nextMap
|
|
},
|
|
mergeProcessCodeLabelMapByObject (labelMap) {
|
|
if (!labelMap || typeof labelMap !== 'object') {
|
|
return
|
|
}
|
|
const nextMap = Object.assign({}, this.processCodeLabelMap || {})
|
|
Object.keys(labelMap).forEach(rawCode => {
|
|
const code = rawCode == null ? '' : String(rawCode).trim()
|
|
const label = labelMap[rawCode] == null ? '' : String(labelMap[rawCode]).trim()
|
|
if (!code || !label) {
|
|
return
|
|
}
|
|
// 兼容后端已保存大小写不一致的编码。
|
|
nextMap[code] = label
|
|
nextMap[code.toLowerCase()] = label
|
|
})
|
|
this.processCodeLabelMap = nextMap
|
|
},
|
|
buildProcessCategoryOptionsByRows (rows) {
|
|
const sourceRows = Array.isArray(rows) ? rows : []
|
|
const nextOptions = []
|
|
const usedCodeMap = {}
|
|
sourceRows.forEach((item, index) => {
|
|
const categoryCode = item && item.categoryCode != null ? String(item.categoryCode).trim() : ''
|
|
const categoryName = item && item.categoryName != null ? String(item.categoryName).trim() : ''
|
|
if (!categoryCode || !categoryName) {
|
|
return
|
|
}
|
|
const codeKey = categoryCode.toLowerCase()
|
|
if (usedCodeMap[codeKey]) {
|
|
return
|
|
}
|
|
usedCodeMap[codeKey] = true
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
nextOptions.push({
|
|
categoryCode: categoryCode,
|
|
categoryName: categoryName,
|
|
sortNo: Number.isNaN(parsedSortNo) ? (index + 1) * 10 : parsedSortNo
|
|
})
|
|
})
|
|
nextOptions.sort((a, b) => {
|
|
if (a.sortNo !== b.sortNo) {
|
|
return a.sortNo - b.sortNo
|
|
}
|
|
return String(a.categoryCode).localeCompare(String(b.categoryCode))
|
|
})
|
|
return nextOptions
|
|
},
|
|
getProcessCategoryOptionsByBuNo (buNo) {
|
|
const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site)
|
|
if (!normalizedBuNo) {
|
|
return []
|
|
}
|
|
const optionMap = this.buProcessCategoryOptionsMap || {}
|
|
return Array.isArray(optionMap[normalizedBuNo]) ? optionMap[normalizedBuNo] : []
|
|
},
|
|
getBuProcessCategoryNameByCode (buNo, categoryCode) {
|
|
if (!categoryCode) {
|
|
return ''
|
|
}
|
|
const normalizedCategoryCode = String(categoryCode).trim()
|
|
if (!normalizedCategoryCode) {
|
|
return ''
|
|
}
|
|
const lowerCategoryCode = normalizedCategoryCode.toLowerCase()
|
|
const configEntry = this.getBuProcessConfigEntry(buNo, this.$store.state.user.site)
|
|
const categoryNameMap = configEntry && configEntry.config && configEntry.config.processCategoryNameMap
|
|
? configEntry.config.processCategoryNameMap
|
|
: {}
|
|
if (categoryNameMap && typeof categoryNameMap === 'object') {
|
|
if (categoryNameMap[normalizedCategoryCode]) {
|
|
return categoryNameMap[normalizedCategoryCode]
|
|
}
|
|
if (categoryNameMap[lowerCategoryCode]) {
|
|
return categoryNameMap[lowerCategoryCode]
|
|
}
|
|
const matchedNameCode = Object.keys(categoryNameMap).find(code => {
|
|
return String(code).trim().toLowerCase() === lowerCategoryCode
|
|
})
|
|
if (matchedNameCode) {
|
|
return categoryNameMap[matchedNameCode] || ''
|
|
}
|
|
}
|
|
const options = this.getProcessCategoryOptionsByBuNo(buNo)
|
|
const matched = options.find(item => {
|
|
if (!item || !item.categoryCode) {
|
|
return false
|
|
}
|
|
return String(item.categoryCode).trim().toLowerCase() === lowerCategoryCode
|
|
})
|
|
return matched && matched.categoryName ? matched.categoryName : ''
|
|
},
|
|
getBuProcessCategoryCodeByProcessCode (buNo, processCode) {
|
|
const configEntry = this.getBuProcessConfigEntry(buNo, this.$store.state.user.site)
|
|
const config = configEntry ? configEntry.config : null
|
|
if (!config || !config.processCategoryMap || typeof config.processCategoryMap !== 'object' || !processCode) {
|
|
return ''
|
|
}
|
|
const sourceMap = config.processCategoryMap
|
|
const normalizedProcessCode = String(processCode).trim()
|
|
if (!normalizedProcessCode) {
|
|
return ''
|
|
}
|
|
if (sourceMap[normalizedProcessCode] != null) {
|
|
return String(sourceMap[normalizedProcessCode]).trim()
|
|
}
|
|
const lowerProcessCode = normalizedProcessCode.toLowerCase()
|
|
const matchedKey = Object.keys(sourceMap).find(code => {
|
|
return String(code).trim().toLowerCase() === lowerProcessCode
|
|
})
|
|
return matchedKey ? String(sourceMap[matchedKey] || '').trim() : ''
|
|
},
|
|
getBuProcessCategoryCode (row, processCode) {
|
|
if (!row || !processCode) {
|
|
return ''
|
|
}
|
|
const map = row.processCategoryMap && typeof row.processCategoryMap === 'object'
|
|
? row.processCategoryMap
|
|
: {}
|
|
const directValue = map[processCode]
|
|
if (directValue != null) {
|
|
return String(directValue).trim()
|
|
}
|
|
const normalizedCode = String(processCode).trim().toLowerCase()
|
|
const matchedKey = Object.keys(map).find(code => String(code).trim().toLowerCase() === normalizedCode)
|
|
if (!matchedKey) {
|
|
return ''
|
|
}
|
|
const matchedValue = map[matchedKey]
|
|
return matchedValue == null ? '' : String(matchedValue).trim()
|
|
},
|
|
normalizeProcessCategoryMapForRow (row) {
|
|
const resultMap = {}
|
|
if (!row) {
|
|
return resultMap
|
|
}
|
|
const validOrderCodes = this.getValidProcessOrderCodes(row.processOrderCodes)
|
|
validOrderCodes.forEach(code => {
|
|
const categoryCode = this.getBuProcessCategoryCode(row, code)
|
|
resultMap[code] = categoryCode || ''
|
|
})
|
|
return resultMap
|
|
},
|
|
handleBuProcessCategoryChange (row, processCode, categoryCode) {
|
|
if (!row || !processCode) {
|
|
return
|
|
}
|
|
const normalizedCode = String(processCode).trim()
|
|
if (!normalizedCode) {
|
|
return
|
|
}
|
|
if (!row.processCategoryMap || typeof row.processCategoryMap !== 'object') {
|
|
this.$set(row, 'processCategoryMap', {})
|
|
}
|
|
const categoryCodeVal = categoryCode == null ? '' : String(categoryCode).trim()
|
|
this.$set(row.processCategoryMap, normalizedCode, categoryCodeVal)
|
|
},
|
|
loadBuProcessCategoryOptionsByBuNo (buNo, silent = true) {
|
|
const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site)
|
|
if (!normalizedBuNo) {
|
|
return Promise.resolve([])
|
|
}
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
buNo: normalizedBuNo,
|
|
userName: this.$store.state.user.name
|
|
}
|
|
return queryProofTrackingProcessCategories(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
if (!silent) {
|
|
this.$message.error((data && data.msg) || `加载事业部【${normalizedBuNo}】工序分类失败`)
|
|
}
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, [])
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: {}
|
|
}))
|
|
}
|
|
return []
|
|
}
|
|
const options = this.buildProcessCategoryOptionsByRows(data.rows || [])
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, options)
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
const processCategoryNameMap = {}
|
|
options.forEach(item => {
|
|
const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : ''
|
|
const categoryName = item && item.categoryName ? String(item.categoryName).trim() : ''
|
|
if (!categoryCode || !categoryName) {
|
|
return
|
|
}
|
|
processCategoryNameMap[categoryCode] = categoryName
|
|
processCategoryNameMap[categoryCode.toLowerCase()] = categoryName
|
|
})
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: processCategoryNameMap
|
|
}))
|
|
}
|
|
return options
|
|
}).catch(() => {
|
|
if (!silent) {
|
|
this.$message.error(`加载事业部【${normalizedBuNo}】工序分类异常`)
|
|
}
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, [])
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: {}
|
|
}))
|
|
}
|
|
return []
|
|
})
|
|
},
|
|
loadAllBuProcessCategoryOptions (sourceRows, silent = true) {
|
|
const rows = Array.isArray(sourceRows) ? sourceRows : []
|
|
const requestBuNos = []
|
|
const usedBuMap = {}
|
|
rows.forEach(item => {
|
|
const buNo = this.normalizeBuNo(item && item.buNo ? item.buNo : '', this.$store.state.user.site)
|
|
if (!buNo || usedBuMap[buNo]) {
|
|
return
|
|
}
|
|
usedBuMap[buNo] = true
|
|
requestBuNos.push(buNo)
|
|
})
|
|
if (requestBuNos.length === 0) {
|
|
return Promise.resolve([])
|
|
}
|
|
return Promise.all(requestBuNos.map(buNo => this.loadBuProcessCategoryOptionsByBuNo(buNo, silent)))
|
|
},
|
|
getProcessLabelByCode (processCode) {
|
|
if (!processCode) {
|
|
return ''
|
|
}
|
|
const normalizedCode = String(processCode).trim()
|
|
const lowerCode = normalizedCode.toLowerCase()
|
|
const matched = this.processColumns.find(item => item && item.code === normalizedCode)
|
|
if (matched && matched.label) {
|
|
return matched.label
|
|
}
|
|
const caseInsensitiveMatched = this.processColumns.find(item => {
|
|
if (!item || !item.code) {
|
|
return false
|
|
}
|
|
return String(item.code).trim().toLowerCase() === lowerCode
|
|
})
|
|
if (caseInsensitiveMatched && caseInsensitiveMatched.label) {
|
|
return caseInsensitiveMatched.label
|
|
}
|
|
const codeLabelMap = this.processCodeLabelMap || {}
|
|
if (codeLabelMap[normalizedCode]) {
|
|
return codeLabelMap[normalizedCode]
|
|
}
|
|
if (codeLabelMap[lowerCode]) {
|
|
return codeLabelMap[lowerCode]
|
|
}
|
|
const defaultMatched = DEFAULT_PROCESS_COLUMNS.find(item => {
|
|
if (!item || !item.code) {
|
|
return false
|
|
}
|
|
return item.code.toLowerCase() === lowerCode
|
|
})
|
|
return defaultMatched && defaultMatched.label ? defaultMatched.label : normalizedCode
|
|
},
|
|
getValidProcessOrderCodes (codes) {
|
|
// 顺序列表不能因为“当前 BU 不可见”而丢项:先保留后端返回顺序,再补齐当前已知工序。
|
|
const orderedCodes = []
|
|
if (Array.isArray(codes)) {
|
|
codes.forEach(code => {
|
|
const codeVal = code == null ? '' : String(code).trim()
|
|
if (!codeVal || orderedCodes.indexOf(codeVal) > -1) {
|
|
return
|
|
}
|
|
orderedCodes.push(codeVal)
|
|
})
|
|
}
|
|
const allCodes = this.getAllProcessCodes()
|
|
allCodes.forEach(code => {
|
|
if (orderedCodes.indexOf(code) === -1) {
|
|
orderedCodes.push(code)
|
|
}
|
|
})
|
|
return orderedCodes
|
|
},
|
|
normalizeBuNo (buNo, site) {
|
|
const rawBuNo = buNo == null ? '' : String(buNo).trim()
|
|
if (!rawBuNo) {
|
|
return ''
|
|
}
|
|
const splitIdx = rawBuNo.indexOf('_')
|
|
if (splitIdx < 1 || splitIdx >= rawBuNo.length - 1) {
|
|
return rawBuNo
|
|
}
|
|
// BU统一按“去掉site前缀”展示与保存,例如 2_01-Label -> 01-Label。
|
|
return rawBuNo.substring(splitIdx + 1)
|
|
},
|
|
isRfidBuNo (buNo) {
|
|
const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site)
|
|
return normalizedBuNo.toUpperCase() === '03-RFID'
|
|
},
|
|
getValidProcessCodes (codes, allowedCodes) {
|
|
if (!Array.isArray(codes) || codes.length === 0) {
|
|
return []
|
|
}
|
|
const allCodes = Array.isArray(allowedCodes) && allowedCodes.length > 0
|
|
? allowedCodes
|
|
.map(code => (code == null ? '' : String(code).trim()))
|
|
.filter(code => !!code)
|
|
: this.getAllProcessCodes()
|
|
const normalizedCodes = []
|
|
codes.forEach(code => {
|
|
const codeVal = code == null ? '' : String(code).trim()
|
|
if (!codeVal || allCodes.indexOf(codeVal) === -1) {
|
|
return
|
|
}
|
|
if (normalizedCodes.indexOf(codeVal) === -1) {
|
|
normalizedCodes.push(codeVal)
|
|
}
|
|
})
|
|
return normalizedCodes
|
|
},
|
|
getBuProcessConfigEntry (buNo, site) {
|
|
const normalizedBuNo = this.normalizeBuNo(buNo, site)
|
|
if (!normalizedBuNo) {
|
|
return null
|
|
}
|
|
const config = this.buProcessConfigMap[normalizedBuNo]
|
|
if (config) {
|
|
return { key: normalizedBuNo, config: config }
|
|
}
|
|
return null
|
|
},
|
|
getSelectedSearchBuNo () {
|
|
return this.normalizeBuNo(this.searchData.buNo, this.searchData.site)
|
|
},
|
|
handleSearchBuChange () {
|
|
this.ensureSearchBuNoSelected()
|
|
// BU筛选变化后立即重新查询,并回到第一页。
|
|
this.queryProcessColumns().then(() => {
|
|
this.getDataList('Y')
|
|
})
|
|
},
|
|
getProcessVisibleCodesByBuNo (buNo, site) {
|
|
const configEntry = this.getBuProcessConfigEntry(buNo, site)
|
|
const config = configEntry ? configEntry.config : null
|
|
// 没有配置或配置为全部显示时,返回 null 表示该 BU 工序列不过滤。
|
|
if (!config || config.showAll !== false) {
|
|
return null
|
|
}
|
|
const validCodes = this.getValidProcessCodes(config.processCodes)
|
|
if (validCodes.length === 0 || validCodes.length >= this.processColumns.length) {
|
|
return null
|
|
}
|
|
return validCodes
|
|
},
|
|
getProcessColumnWidth (item) {
|
|
const rows = this.dataList.filter(row =>
|
|
this.isProcessColumnVisibleForRow(row, item)
|
|
)
|
|
// 没有数据
|
|
if (rows.length === 0) {
|
|
return 110
|
|
}
|
|
// 有任何一个未完成
|
|
if (rows.some(row => !this.isProcessStatusComplete(row, item))) {
|
|
return 134
|
|
}
|
|
// 全部完成
|
|
return 110
|
|
},
|
|
isProcessColumnVisibleForRow (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return true
|
|
}
|
|
// 已指定查询BU时,列集合已经按查询BU过滤,不再按行二次隐藏。
|
|
if (this.getSelectedSearchBuNo()) {
|
|
return true
|
|
}
|
|
const visibleCodes = this.getProcessVisibleCodesByBuNo(row.buNo, row.site)
|
|
if (!visibleCodes) {
|
|
return true
|
|
}
|
|
return visibleCodes.indexOf(processCol.code) > -1
|
|
},
|
|
queryBuProcessConfig () {
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
userName: this.$store.state.user.name
|
|
}
|
|
return queryProofTrackingBuProcessConfig(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '加载事业部进度展示配置失败')
|
|
return
|
|
}
|
|
const rows = data.rows || []
|
|
const nextMap = {}
|
|
const processLabelMap = {}
|
|
rows.forEach(item => {
|
|
if (item && item.processLabelMap && typeof item.processLabelMap === 'object') {
|
|
Object.keys(item.processLabelMap).forEach(rawCode => {
|
|
const code = rawCode == null ? '' : String(rawCode).trim()
|
|
const label = item.processLabelMap[rawCode] == null ? '' : String(item.processLabelMap[rawCode]).trim()
|
|
if (!code || !label) {
|
|
return
|
|
}
|
|
processLabelMap[code] = label
|
|
})
|
|
}
|
|
const buNo = this.normalizeBuNo(item && item.buNo ? item.buNo : '', this.$store.state.user.site)
|
|
if (!buNo) {
|
|
return
|
|
}
|
|
const processOrderCodes = this.getValidProcessOrderCodes(item.processOrderCodes)
|
|
const processCodes = this.getValidProcessCodes(item.processCodes, processOrderCodes)
|
|
const showAll = item && item.showAll === true
|
|
? true
|
|
: processCodes.length >= processOrderCodes.length
|
|
const selectedCodeMap = {}
|
|
processCodes.forEach(code => {
|
|
selectedCodeMap[code] = true
|
|
})
|
|
const processCategoryMap = {}
|
|
const sourceCategoryMap = item && item.processCategoryMap && typeof item.processCategoryMap === 'object'
|
|
? item.processCategoryMap
|
|
: {}
|
|
const processCategoryNameMap = {}
|
|
const sourceCategoryNameMap = item && item.processCategoryNameMap && typeof item.processCategoryNameMap === 'object'
|
|
? item.processCategoryNameMap
|
|
: {}
|
|
Object.keys(sourceCategoryNameMap).forEach(rawCategoryCode => {
|
|
const categoryCode = rawCategoryCode == null ? '' : String(rawCategoryCode).trim()
|
|
const categoryName = sourceCategoryNameMap[rawCategoryCode] == null
|
|
? ''
|
|
: String(sourceCategoryNameMap[rawCategoryCode]).trim()
|
|
if (!categoryCode || !categoryName) {
|
|
return
|
|
}
|
|
processCategoryNameMap[categoryCode] = categoryName
|
|
processCategoryNameMap[categoryCode.toLowerCase()] = categoryName
|
|
})
|
|
processOrderCodes.forEach(code => {
|
|
if (!code) {
|
|
return
|
|
}
|
|
const directVal = sourceCategoryMap[code]
|
|
if (directVal != null) {
|
|
processCategoryMap[code] = String(directVal).trim()
|
|
return
|
|
}
|
|
const lowerCode = String(code).trim().toLowerCase()
|
|
const matchedKey = Object.keys(sourceCategoryMap).find(rawCode => {
|
|
return String(rawCode).trim().toLowerCase() === lowerCode
|
|
})
|
|
processCategoryMap[code] = matchedKey ? String(sourceCategoryMap[matchedKey] || '').trim() : ''
|
|
})
|
|
nextMap[buNo] = {
|
|
showAll: showAll,
|
|
processOrderCodes: processOrderCodes,
|
|
processCodes: showAll ? processOrderCodes.slice() : processOrderCodes.filter(code => selectedCodeMap[code]),
|
|
processCategoryMap: processCategoryMap,
|
|
processCategoryNameMap: processCategoryNameMap
|
|
}
|
|
})
|
|
this.mergeProcessCodeLabelMapByObject(processLabelMap)
|
|
this.buProcessConfigMap = nextMap
|
|
this.ensureBuProcessConfigRows()
|
|
this.loadAllBuProcessCategoryOptions(this.buProcessConfigRows, true)
|
|
this.refreshTrackingTableLayout()
|
|
}).catch(() => {
|
|
this.$message.error('加载事业部进度展示配置异常')
|
|
})
|
|
},
|
|
ensureBuProcessConfigRows () {
|
|
const allCodes = this.getAllProcessCodes()
|
|
const builtinCodeMap = {}
|
|
DEFAULT_PROCESS_COLUMNS.forEach(item => {
|
|
if (item && item.code) {
|
|
builtinCodeMap[item.code] = true
|
|
}
|
|
})
|
|
const defaultSelectedCodes = allCodes.filter(code => !!builtinCodeMap[code])
|
|
const rows = []
|
|
const usedBuMap = {}
|
|
const appendRow = (buNo, buDesc, site) => {
|
|
const key = this.normalizeBuNo(buNo, site || this.$store.state.user.site)
|
|
if (!key || usedBuMap[key]) {
|
|
return
|
|
}
|
|
usedBuMap[key] = true
|
|
const cfg = this.buProcessConfigMap[key]
|
|
const row = {
|
|
buNo: key,
|
|
buDesc: buDesc || key,
|
|
processOrderCodes: allCodes.slice(),
|
|
// 新增默认工序默认不勾选:仅预勾选系统内置工序,自定义新增项需事业部手工勾选。
|
|
processCodes: (defaultSelectedCodes.length > 0 ? defaultSelectedCodes : allCodes).slice(),
|
|
processCategoryMap: {}
|
|
}
|
|
if (cfg) {
|
|
const validOrderCodes = this.getValidProcessOrderCodes(cfg.processOrderCodes)
|
|
row.processOrderCodes = validOrderCodes
|
|
const validCodes = this.getValidProcessCodes(cfg.processCodes, validOrderCodes)
|
|
if (validCodes.length > 0) {
|
|
const selectedCodeMap = {}
|
|
validCodes.forEach(code => {
|
|
selectedCodeMap[code] = true
|
|
})
|
|
row.processCodes = validOrderCodes.filter(code => selectedCodeMap[code])
|
|
} else {
|
|
const defaultSelectedCodeMap = {}
|
|
row.processCodes.forEach(code => {
|
|
defaultSelectedCodeMap[code] = true
|
|
})
|
|
row.processCodes = validOrderCodes.filter(code => defaultSelectedCodeMap[code])
|
|
}
|
|
row.processCategoryMap = cfg.processCategoryMap && typeof cfg.processCategoryMap === 'object'
|
|
? Object.assign({}, cfg.processCategoryMap)
|
|
: {}
|
|
}
|
|
row.processCategoryMap = this.normalizeProcessCategoryMapForRow(row)
|
|
rows.push(row)
|
|
}
|
|
this.userBuList.forEach(item => {
|
|
if (!item || !item.buNo) {
|
|
return
|
|
}
|
|
appendRow(item.buNo, item.buDesc || this.normalizeBuNo(item.buNo, this.$store.state.user.site), this.$store.state.user.site)
|
|
})
|
|
Object.keys(this.buProcessConfigMap).forEach(buNo => {
|
|
appendRow(buNo, buNo, this.$store.state.user.site)
|
|
})
|
|
this.buProcessConfigRows = rows
|
|
},
|
|
openBuProcessConfigDialog () {
|
|
this.ensureBuProcessConfigRows()
|
|
this.processOrderDragState = {
|
|
buNo: '',
|
|
fromIndex: -1
|
|
}
|
|
this.buProcessConfigDialogVisible = true
|
|
this.loadAllBuProcessCategoryOptions(this.buProcessConfigRows, false)
|
|
},
|
|
buildDefaultProcessConfigRow (item) {
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
const activeFlag = item && item.activeFlag ? String(item.activeFlag).trim().toUpperCase() : 'Y'
|
|
const code = item && item.code ? String(item.code).trim() : ''
|
|
return {
|
|
_rowKey: `default_process_${Date.now()}_${this.defaultProcessConfigRowSeed++}`,
|
|
// 记录后端原始编码,删除时可避免“修改了编码但尚未保存”导致删不到原记录。
|
|
_persistedCode: code,
|
|
sortNo: Number.isNaN(parsedSortNo) || parsedSortNo <= 0 ? this.getNextDefaultProcessSortNo() : parsedSortNo,
|
|
code: code,
|
|
label: item && item.label ? String(item.label).trim() : '',
|
|
activeFlag: activeFlag === 'N' ? 'N' : 'Y'
|
|
}
|
|
},
|
|
getNextDefaultProcessSortNo () {
|
|
if (!Array.isArray(this.defaultProcessConfigRows) || this.defaultProcessConfigRows.length === 0) {
|
|
return 10
|
|
}
|
|
let maxSortNo = 0
|
|
this.defaultProcessConfigRows.forEach(row => {
|
|
const parsedSortNo = parseInt(row && row.sortNo, 10)
|
|
if (!Number.isNaN(parsedSortNo) && parsedSortNo > maxSortNo) {
|
|
maxSortNo = parsedSortNo
|
|
}
|
|
})
|
|
return maxSortNo > 0 ? maxSortNo + 10 : (this.defaultProcessConfigRows.length + 1) * 10
|
|
},
|
|
resetDefaultProcessConfigSortNoByOrder () {
|
|
if (!Array.isArray(this.defaultProcessConfigRows)) {
|
|
return
|
|
}
|
|
this.defaultProcessConfigRows.forEach((row, index) => {
|
|
if (!row) {
|
|
return
|
|
}
|
|
this.$set(row, 'sortNo', (index + 1) * 10)
|
|
})
|
|
},
|
|
clearDefaultProcessConfigSelection () {
|
|
this.defaultProcessConfigSelectionRows = []
|
|
this.$nextTick(() => {
|
|
const tableRef = this.$refs.defaultProcessConfigTable
|
|
if (tableRef && tableRef.clearSelection) {
|
|
tableRef.clearSelection()
|
|
}
|
|
})
|
|
},
|
|
destroyDefaultProcessConfigSortable () {
|
|
if (this.defaultProcessConfigSortable && typeof this.defaultProcessConfigSortable.destroy === 'function') {
|
|
this.defaultProcessConfigSortable.destroy()
|
|
}
|
|
this.defaultProcessConfigSortable = null
|
|
},
|
|
initDefaultProcessConfigSortable () {
|
|
this.$nextTick(() => {
|
|
this.destroyDefaultProcessConfigSortable()
|
|
if (!this.defaultProcessConfigDialogVisible || !Array.isArray(this.defaultProcessConfigRows) || this.defaultProcessConfigRows.length === 0) {
|
|
return
|
|
}
|
|
const tableRef = this.$refs.defaultProcessConfigTable
|
|
const tableBody = tableRef && tableRef.$el
|
|
? tableRef.$el.querySelector('.el-table__body-wrapper tbody')
|
|
: null
|
|
if (!tableBody) {
|
|
return
|
|
}
|
|
this.defaultProcessConfigSortable = Sortable.create(tableBody, {
|
|
animation: 150,
|
|
// 整行可拖拽,避免必须精确悬浮到小图标才能排序。
|
|
handle: 'tr',
|
|
draggable: 'tr',
|
|
ghostClass: 'default-process-sortable-ghost',
|
|
chosenClass: 'default-process-sortable-chosen',
|
|
dragClass: 'default-process-sortable-drag',
|
|
onEnd: (evt) => {
|
|
const oldIndex = evt && typeof evt.oldIndex === 'number' ? evt.oldIndex : -1
|
|
const newIndex = evt && typeof evt.newIndex === 'number' ? evt.newIndex : -1
|
|
if (oldIndex < 0 || newIndex < 0 || oldIndex === newIndex) {
|
|
return
|
|
}
|
|
const movedRow = this.defaultProcessConfigRows.splice(oldIndex, 1)[0]
|
|
this.defaultProcessConfigRows.splice(newIndex, 0, movedRow)
|
|
this.resetDefaultProcessConfigSortNoByOrder()
|
|
this.clearDefaultProcessConfigSelection()
|
|
}
|
|
})
|
|
})
|
|
},
|
|
handleDefaultProcessConfigDialogClosed () {
|
|
this.defaultProcessConfigDeleteLoading = false
|
|
this.clearDefaultProcessConfigSelection()
|
|
this.destroyDefaultProcessConfigSortable()
|
|
},
|
|
handleDefaultProcessConfigSelectionChange (rows) {
|
|
this.defaultProcessConfigSelectionRows = Array.isArray(rows) ? rows : []
|
|
},
|
|
normalizeDefaultProcessFieldBaseByCode (code) {
|
|
const rawCode = code == null ? '' : String(code).trim()
|
|
if (!rawCode) {
|
|
return ''
|
|
}
|
|
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(rawCode)) {
|
|
return rawCode
|
|
}
|
|
const parts = rawCode.split(/[^A-Za-z0-9]+/).filter(item => !!item)
|
|
if (parts.length === 0) {
|
|
return ''
|
|
}
|
|
const firstPart = parts[0].toLowerCase()
|
|
const remained = parts.slice(1).map(item => {
|
|
const lower = item.toLowerCase()
|
|
return `${lower.charAt(0).toUpperCase()}${lower.substring(1)}`
|
|
}).join('')
|
|
let fieldBase = `${firstPart}${remained}`
|
|
if (/^[0-9]/.test(fieldBase)) {
|
|
fieldBase = `p${fieldBase}`
|
|
}
|
|
return fieldBase
|
|
},
|
|
buildDefaultProcessFieldsByCode (code) {
|
|
const fieldBase = this.normalizeDefaultProcessFieldBaseByCode(code)
|
|
if (!fieldBase) {
|
|
return {
|
|
planField: '',
|
|
actualField: '',
|
|
statusField: ''
|
|
}
|
|
}
|
|
return {
|
|
planField: `${fieldBase}PlanDate`,
|
|
actualField: `${fieldBase}ActualDate`,
|
|
statusField: `${fieldBase}Status`
|
|
}
|
|
},
|
|
queryDefaultProcessConfig () {
|
|
const inData = {
|
|
site: '*',
|
|
buNo: '*',
|
|
userName: this.$store.state.user.name
|
|
}
|
|
return queryProofTrackingProcessColumnsManage(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '加载默认工序配置失败')
|
|
this.defaultProcessConfigRows = []
|
|
this.defaultProcessConfigSelectionRows = []
|
|
return []
|
|
}
|
|
const rows = Array.isArray(data.rows) ? data.rows : []
|
|
const sortedRows = rows
|
|
.map((item, index) => {
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
return {
|
|
sourceIndex: index,
|
|
sortNo: Number.isNaN(parsedSortNo) ? Number.MAX_SAFE_INTEGER : parsedSortNo,
|
|
item: item
|
|
}
|
|
})
|
|
.sort((a, b) => {
|
|
if (a.sortNo !== b.sortNo) {
|
|
return a.sortNo - b.sortNo
|
|
}
|
|
return a.sourceIndex - b.sourceIndex
|
|
})
|
|
this.defaultProcessConfigRows = sortedRows.map(({ item }) => this.buildDefaultProcessConfigRow(item))
|
|
this.resetDefaultProcessConfigSortNoByOrder()
|
|
this.clearDefaultProcessConfigSelection()
|
|
this.initDefaultProcessConfigSortable()
|
|
return this.defaultProcessConfigRows
|
|
}).catch(() => {
|
|
this.$message.error('加载默认工序配置异常')
|
|
this.defaultProcessConfigRows = []
|
|
this.defaultProcessConfigSelectionRows = []
|
|
this.destroyDefaultProcessConfigSortable()
|
|
return []
|
|
})
|
|
},
|
|
openDefaultProcessConfigDialog () {
|
|
this.defaultProcessConfigDeleteLoading = false
|
|
this.defaultProcessConfigDialogVisible = true
|
|
this.queryDefaultProcessConfig()
|
|
},
|
|
buildProcessCategoryRow (item) {
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : ''
|
|
return {
|
|
_rowKey: `process_category_${Date.now()}_${this.processCategoryRowSeed++}`,
|
|
_persistedCode: categoryCode,
|
|
sortNo: Number.isNaN(parsedSortNo) || parsedSortNo <= 0 ? (this.processCategoryRows.length + 1) * 10 : parsedSortNo,
|
|
categoryCode: categoryCode,
|
|
categoryName: item && item.categoryName ? String(item.categoryName).trim() : ''
|
|
}
|
|
},
|
|
queryProcessCategoryConfig (buNo, silent = false) {
|
|
const normalizedBuNo = this.normalizeBuNo(buNo, this.$store.state.user.site)
|
|
if (!normalizedBuNo) {
|
|
return Promise.resolve([])
|
|
}
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
buNo: normalizedBuNo,
|
|
userName: this.$store.state.user.name
|
|
}
|
|
return queryProofTrackingProcessCategories(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
if (!silent) {
|
|
this.$message.error((data && data.msg) || '加载工序分类失败')
|
|
}
|
|
this.processCategoryRows = []
|
|
this.processCategorySelectionRows = []
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, [])
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: {}
|
|
}))
|
|
}
|
|
return []
|
|
}
|
|
const rows = Array.isArray(data.rows) ? data.rows : []
|
|
const categoryOptions = this.buildProcessCategoryOptionsByRows(rows)
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, categoryOptions)
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
const processCategoryNameMap = {}
|
|
categoryOptions.forEach(item => {
|
|
const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : ''
|
|
const categoryName = item && item.categoryName ? String(item.categoryName).trim() : ''
|
|
if (!categoryCode || !categoryName) {
|
|
return
|
|
}
|
|
processCategoryNameMap[categoryCode] = categoryName
|
|
processCategoryNameMap[categoryCode.toLowerCase()] = categoryName
|
|
})
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: processCategoryNameMap
|
|
}))
|
|
}
|
|
const sortedRows = rows
|
|
.map((item, index) => {
|
|
const parsedSortNo = parseInt(item && item.sortNo, 10)
|
|
return {
|
|
sourceIndex: index,
|
|
sortNo: Number.isNaN(parsedSortNo) ? Number.MAX_SAFE_INTEGER : parsedSortNo,
|
|
item: item
|
|
}
|
|
})
|
|
.sort((a, b) => {
|
|
if (a.sortNo !== b.sortNo) {
|
|
return a.sortNo - b.sortNo
|
|
}
|
|
return a.sourceIndex - b.sourceIndex
|
|
})
|
|
this.processCategoryRows = sortedRows.map(({ item }) => this.buildProcessCategoryRow(item))
|
|
this.processCategorySelectionRows = []
|
|
return this.processCategoryRows
|
|
}).catch(() => {
|
|
if (!silent) {
|
|
this.$message.error('加载工序分类异常')
|
|
}
|
|
this.processCategoryRows = []
|
|
this.processCategorySelectionRows = []
|
|
this.$set(this.buProcessCategoryOptionsMap, normalizedBuNo, [])
|
|
if (this.buProcessConfigMap && this.buProcessConfigMap[normalizedBuNo]) {
|
|
this.$set(this.buProcessConfigMap, normalizedBuNo, Object.assign({}, this.buProcessConfigMap[normalizedBuNo], {
|
|
processCategoryNameMap: {}
|
|
}))
|
|
}
|
|
return []
|
|
})
|
|
},
|
|
openProcessCategoryDialog (row) {
|
|
const buNo = this.normalizeBuNo(row && row.buNo ? row.buNo : '', this.$store.state.user.site)
|
|
if (!buNo) {
|
|
this.$message.warning('请在事业部进度设置中指定事业部后再维护分类')
|
|
return
|
|
}
|
|
this.processCategoryDialogBuNo = buNo
|
|
this.processCategoryDialogBuDesc = row && row.buDesc ? row.buDesc : buNo
|
|
this.processCategoryDialogVisible = true
|
|
this.processCategorySaveLoading = false
|
|
this.queryProcessCategoryConfig(buNo)
|
|
},
|
|
handleProcessCategoryDialogClosed () {
|
|
this.processCategoryRows = []
|
|
this.processCategorySelectionRows = []
|
|
this.processCategoryDialogBuNo = ''
|
|
this.processCategoryDialogBuDesc = ''
|
|
this.processCategorySaveLoading = false
|
|
},
|
|
handleProcessCategorySelectionChange (rows) {
|
|
this.processCategorySelectionRows = Array.isArray(rows) ? rows : []
|
|
},
|
|
addProcessCategoryRow () {
|
|
this.processCategoryRows.push(this.buildProcessCategoryRow({
|
|
sortNo: (this.processCategoryRows.length + 1) * 10
|
|
}))
|
|
},
|
|
deleteProcessCategoryRows () {
|
|
if (!Array.isArray(this.processCategorySelectionRows) || this.processCategorySelectionRows.length === 0) {
|
|
this.$message.warning('请先勾选要删除的分类')
|
|
return
|
|
}
|
|
const selectedRows = this.processCategorySelectionRows.slice()
|
|
this.$confirm(`确认删除选中的 ${selectedRows.length} 条分类吗?`, '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
const selectedKeyMap = {}
|
|
selectedRows.forEach(item => {
|
|
if (item && item._rowKey) {
|
|
selectedKeyMap[item._rowKey] = true
|
|
}
|
|
})
|
|
const nextRows = this.processCategoryRows.filter(item => !(item && selectedKeyMap[item._rowKey]))
|
|
const submitRows = this.normalizeProcessCategoryRowsForSave(nextRows)
|
|
if (!submitRows) {
|
|
return
|
|
}
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
buNo: this.processCategoryDialogBuNo,
|
|
updateBy: this.$store.state.user.name,
|
|
rows: submitRows
|
|
}
|
|
this.processCategorySaveLoading = true
|
|
saveProofTrackingProcessCategoriesManage(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '删除工序分类失败')
|
|
return
|
|
}
|
|
this.handleProcessCategoryConfigSaved(data.msg || '删除成功')
|
|
}).catch(() => {
|
|
this.$message.error('删除工序分类异常')
|
|
}).finally(() => {
|
|
this.processCategorySaveLoading = false
|
|
})
|
|
}).catch(() => {
|
|
// 用户取消删除时无需额外提示。
|
|
})
|
|
},
|
|
normalizeProcessCategoryRowsForSave (sourceRowsInput) {
|
|
const sourceRows = Array.isArray(sourceRowsInput)
|
|
? sourceRowsInput
|
|
: (Array.isArray(this.processCategoryRows) ? this.processCategoryRows : [])
|
|
const normalizedRows = []
|
|
const usedNameMap = {}
|
|
for (let i = 0; i < sourceRows.length; i++) {
|
|
const row = sourceRows[i] || {}
|
|
const rowNo = i + 1
|
|
const categoryCode = row.categoryCode == null ? '' : String(row.categoryCode).trim()
|
|
const categoryName = row.categoryName == null ? '' : String(row.categoryName).trim()
|
|
if (!categoryName) {
|
|
this.$message.warning(`第${rowNo}行的分类名称不能为空`)
|
|
return null
|
|
}
|
|
const nameKey = categoryName.toLowerCase()
|
|
if (usedNameMap[nameKey]) {
|
|
this.$message.warning(`分类名称【${categoryName}】重复,请检查`)
|
|
return null
|
|
}
|
|
usedNameMap[nameKey] = true
|
|
normalizedRows.push({
|
|
// 旧分类携带隐藏编码回传,便于后端识别“保留/新增/删除”并做使用校验。
|
|
categoryCode: categoryCode || null,
|
|
categoryName: categoryName,
|
|
sortNo: rowNo * 10
|
|
})
|
|
}
|
|
return normalizedRows
|
|
},
|
|
handleProcessCategoryConfigSaved (successMsg) {
|
|
this.$message.success(successMsg || '工序分类已保存')
|
|
this.queryProcessCategoryConfig(this.processCategoryDialogBuNo, true).then(() => {
|
|
const buNo = this.processCategoryDialogBuNo
|
|
const validCategoryCodeMap = {}
|
|
this.getProcessCategoryOptionsByBuNo(buNo).forEach(item => {
|
|
if (item && item.categoryCode) {
|
|
validCategoryCodeMap[String(item.categoryCode).trim()] = true
|
|
}
|
|
})
|
|
this.buProcessConfigRows.forEach(configRow => {
|
|
const rowBuNo = this.normalizeBuNo(configRow && configRow.buNo ? configRow.buNo : '', this.$store.state.user.site)
|
|
if (!rowBuNo || rowBuNo !== buNo) {
|
|
return
|
|
}
|
|
const nextCategoryMap = this.normalizeProcessCategoryMapForRow(configRow)
|
|
Object.keys(nextCategoryMap).forEach(processCode => {
|
|
const categoryCode = nextCategoryMap[processCode]
|
|
if (!categoryCode || validCategoryCodeMap[categoryCode]) {
|
|
return
|
|
}
|
|
nextCategoryMap[processCode] = ''
|
|
})
|
|
this.$set(configRow, 'processCategoryMap', nextCategoryMap)
|
|
})
|
|
this.queryProcessColumns().then(() => {
|
|
this.refreshTrackingTableLayout()
|
|
})
|
|
})
|
|
},
|
|
saveProcessCategoryConfig () {
|
|
const submitRows = this.normalizeProcessCategoryRowsForSave()
|
|
if (!submitRows) {
|
|
return
|
|
}
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
buNo: this.processCategoryDialogBuNo,
|
|
updateBy: this.$store.state.user.name,
|
|
rows: submitRows
|
|
}
|
|
this.processCategorySaveLoading = true
|
|
saveProofTrackingProcessCategoriesManage(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '工序分类保存失败')
|
|
return
|
|
}
|
|
this.handleProcessCategoryConfigSaved(data.msg || '工序分类已保存')
|
|
}).catch(() => {
|
|
this.$message.error('工序分类保存异常')
|
|
}).finally(() => {
|
|
this.processCategorySaveLoading = false
|
|
})
|
|
},
|
|
addDefaultProcessConfigRow () {
|
|
this.defaultProcessConfigRows.push(this.buildDefaultProcessConfigRow({
|
|
sortNo: this.getNextDefaultProcessSortNo(),
|
|
activeFlag: 'Y'
|
|
}))
|
|
this.resetDefaultProcessConfigSortNoByOrder()
|
|
this.initDefaultProcessConfigSortable()
|
|
},
|
|
deleteDefaultProcessConfigRow () {
|
|
if (!this.defaultProcessConfigSelectionRows || this.defaultProcessConfigSelectionRows.length === 0) {
|
|
this.$message.warning('请先勾选要删除的行')
|
|
return
|
|
}
|
|
const selectedRows = this.defaultProcessConfigSelectionRows.slice()
|
|
this.$confirm(`确认删除选中的 ${selectedRows.length} 条默认工序吗?`, '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
const selectedKeyMap = {}
|
|
const persistedCodeMap = {}
|
|
selectedRows.forEach(item => {
|
|
if (item && item._rowKey) {
|
|
selectedKeyMap[item._rowKey] = true
|
|
}
|
|
const persistedCode = item && item._persistedCode ? String(item._persistedCode).trim() : ''
|
|
const persistedCodeKey = persistedCode.toLowerCase()
|
|
if (persistedCode && !persistedCodeMap[persistedCodeKey]) {
|
|
persistedCodeMap[persistedCodeKey] = persistedCode
|
|
}
|
|
})
|
|
const nextRows = this.defaultProcessConfigRows.filter(row => {
|
|
return !(row && row._rowKey && selectedKeyMap[row._rowKey])
|
|
})
|
|
const applyDeletedRowsToUi = () => {
|
|
this.defaultProcessConfigRows = nextRows
|
|
this.resetDefaultProcessConfigSortNoByOrder()
|
|
this.clearDefaultProcessConfigSelection()
|
|
this.initDefaultProcessConfigSortable()
|
|
}
|
|
const persistedCodes = Object.keys(persistedCodeMap).map(key => persistedCodeMap[key])
|
|
if (persistedCodes.length === 0) {
|
|
applyDeletedRowsToUi()
|
|
this.$message.success('删除成功')
|
|
return
|
|
}
|
|
const inData = {
|
|
site: '*',
|
|
buNo: '*',
|
|
updateBy: this.$store.state.user.name,
|
|
processCodes: persistedCodes
|
|
}
|
|
this.defaultProcessConfigDeleteLoading = true
|
|
deleteProofTrackingProcessColumnsManage(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '删除默认工序失败')
|
|
return
|
|
}
|
|
applyDeletedRowsToUi()
|
|
this.$message.success(data.msg || '删除成功')
|
|
// 删除动作实时落库后立即刷新主列表列定义,避免页面继续展示已删工序。
|
|
this.queryProcessColumns().then(() => {
|
|
this.queryBuProcessConfig()
|
|
this.getDataList()
|
|
this.refreshTrackingTableLayout()
|
|
})
|
|
}).catch(() => {
|
|
this.$message.error('删除默认工序异常')
|
|
}).finally(() => {
|
|
this.defaultProcessConfigDeleteLoading = false
|
|
})
|
|
}).catch(() => {
|
|
// 用户取消删除时无需额外提示。
|
|
})
|
|
},
|
|
normalizeDefaultProcessConfigRowsForSave () {
|
|
const sourceRows = Array.isArray(this.defaultProcessConfigRows) ? this.defaultProcessConfigRows : []
|
|
const normalizedRows = []
|
|
const usedCodeMap = {}
|
|
for (let i = 0; i < sourceRows.length; i++) {
|
|
const row = sourceRows[i] || {}
|
|
const rowNo = i + 1
|
|
const code = row.code == null ? '' : String(row.code).trim()
|
|
const codeKey = code.toLowerCase()
|
|
const label = row.label == null ? '' : String(row.label).trim()
|
|
if (!code) {
|
|
this.$message.warning(`第${rowNo}行的工序编码不能为空`)
|
|
return null
|
|
}
|
|
if (usedCodeMap[codeKey]) {
|
|
this.$message.warning(`工序编码【${code}】重复,请检查`)
|
|
return null
|
|
}
|
|
if (!label) {
|
|
this.$message.warning(`第${rowNo}行的工序名称不能为空`)
|
|
return null
|
|
}
|
|
const fieldData = this.buildDefaultProcessFieldsByCode(code)
|
|
if (!fieldData.planField || !fieldData.actualField || !fieldData.statusField) {
|
|
this.$message.warning(`第${rowNo}行的工序编码格式无法生成字段,请调整编码`)
|
|
return null
|
|
}
|
|
usedCodeMap[codeKey] = true
|
|
normalizedRows.push({
|
|
code: code,
|
|
label: label,
|
|
planField: fieldData.planField,
|
|
actualField: fieldData.actualField,
|
|
statusField: fieldData.statusField,
|
|
// 用户要求:默认进度设置不维护工序分类。
|
|
processCategoryCode: null,
|
|
sortNo: rowNo * 10,
|
|
activeFlag: String(row.activeFlag || 'Y').toUpperCase() === 'N' ? 'N' : 'Y'
|
|
})
|
|
}
|
|
return normalizedRows
|
|
},
|
|
saveDefaultProcessConfig () {
|
|
const submitRows = this.normalizeDefaultProcessConfigRowsForSave()
|
|
if (!submitRows) {
|
|
return
|
|
}
|
|
const executeSave = () => {
|
|
const inData = {
|
|
site: '*',
|
|
buNo: '*',
|
|
updateBy: this.$store.state.user.name,
|
|
rows: submitRows
|
|
}
|
|
saveProofTrackingProcessColumnsManage(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '默认工序配置保存失败')
|
|
return
|
|
}
|
|
this.$message.success(data.msg || '默认工序配置已保存')
|
|
this.queryDefaultProcessConfig()
|
|
// 保存后刷新页面列定义,确保新增/删除工序立即反映到主表列。
|
|
this.queryProcessColumns().then(() => {
|
|
this.queryBuProcessConfig()
|
|
this.getDataList()
|
|
this.refreshTrackingTableLayout()
|
|
})
|
|
}).catch(() => {
|
|
this.$message.error('默认工序配置保存异常')
|
|
})
|
|
}
|
|
if (submitRows.length === 0) {
|
|
this.$confirm('当前将清空默认工序配置,保存后页面会回退到系统兜底配置,是否继续?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
executeSave()
|
|
}).catch(() => {
|
|
// 用户取消清空时无需额外提示。
|
|
})
|
|
return
|
|
}
|
|
executeSave()
|
|
},
|
|
refreshTrackingTableLayout () {
|
|
this.$nextTick(() => {
|
|
const tableRef = this.$refs.trackingTable
|
|
if (tableRef && tableRef.doLayout) {
|
|
tableRef.doLayout()
|
|
}
|
|
})
|
|
},
|
|
handleBuProcessCodesChange (row) {
|
|
if (!row) {
|
|
return
|
|
}
|
|
const validOrderCodes = this.getValidProcessOrderCodes(row.processOrderCodes)
|
|
const validCodes = this.getValidProcessCodes(row.processCodes, validOrderCodes)
|
|
const selectedCodeMap = {}
|
|
validCodes.forEach(code => {
|
|
selectedCodeMap[code] = true
|
|
})
|
|
row.processOrderCodes = validOrderCodes
|
|
row.processCodes = validOrderCodes.filter(code => selectedCodeMap[code])
|
|
row.processCategoryMap = this.normalizeProcessCategoryMapForRow(row)
|
|
},
|
|
isProcessOrderDraggingItem (row, index) {
|
|
if (!row || index == null) {
|
|
return false
|
|
}
|
|
return this.processOrderDragState.buNo === row.buNo && this.processOrderDragState.fromIndex === index
|
|
},
|
|
handleProcessOrderDragStart (row, index, event) {
|
|
if (!row || index == null || index < 0) {
|
|
return
|
|
}
|
|
this.processOrderDragState = {
|
|
buNo: row.buNo || '',
|
|
fromIndex: index
|
|
}
|
|
if (event && event.dataTransfer) {
|
|
event.dataTransfer.effectAllowed = 'move'
|
|
}
|
|
},
|
|
handleProcessOrderDrop (row, targetIndex, event) {
|
|
if (event && event.dataTransfer) {
|
|
event.dataTransfer.dropEffect = 'move'
|
|
}
|
|
if (!row || targetIndex == null || targetIndex < 0) {
|
|
this.handleProcessOrderDragEnd()
|
|
return
|
|
}
|
|
const dragBuNo = this.processOrderDragState.buNo
|
|
const fromIndex = Number(this.processOrderDragState.fromIndex)
|
|
if (!dragBuNo || dragBuNo !== row.buNo || Number.isNaN(fromIndex) || fromIndex < 0) {
|
|
this.handleProcessOrderDragEnd()
|
|
return
|
|
}
|
|
const validOrderCodes = this.getValidProcessOrderCodes(row.processOrderCodes)
|
|
if (fromIndex >= validOrderCodes.length || targetIndex >= validOrderCodes.length) {
|
|
this.handleProcessOrderDragEnd()
|
|
return
|
|
}
|
|
if (fromIndex === targetIndex) {
|
|
this.handleProcessOrderDragEnd()
|
|
return
|
|
}
|
|
const movedCode = validOrderCodes.splice(fromIndex, 1)[0]
|
|
const insertIndex = targetIndex
|
|
validOrderCodes.splice(insertIndex, 0, movedCode)
|
|
row.processOrderCodes = validOrderCodes
|
|
this.handleBuProcessCodesChange(row)
|
|
this.handleProcessOrderDragEnd()
|
|
},
|
|
handleProcessOrderDragEnd () {
|
|
this.processOrderDragState = {
|
|
buNo: '',
|
|
fromIndex: -1
|
|
}
|
|
},
|
|
saveBuProcessConfig () {
|
|
if (!this.buProcessConfigRows || this.buProcessConfigRows.length === 0) {
|
|
this.$message.warning('暂无可配置的事业部')
|
|
return
|
|
}
|
|
const nextMap = {}
|
|
const submitRows = []
|
|
for (let i = 0; i < this.buProcessConfigRows.length; i++) {
|
|
const row = this.buProcessConfigRows[i]
|
|
const buNo = this.normalizeBuNo(row && row.buNo ? row.buNo : '', this.$store.state.user.site)
|
|
if (!buNo) {
|
|
continue
|
|
}
|
|
const validOrderCodes = this.getValidProcessOrderCodes(row.processOrderCodes)
|
|
const selectedCodeMap = {}
|
|
this.getValidProcessCodes(row.processCodes, validOrderCodes).forEach(code => {
|
|
selectedCodeMap[code] = true
|
|
})
|
|
const validCodes = validOrderCodes.filter(code => selectedCodeMap[code])
|
|
if (validCodes.length === 0) {
|
|
this.$message.warning(`事业部【${row.buDesc || buNo}】至少需要选择一个可见进度`)
|
|
return
|
|
}
|
|
const processCategoryMap = this.normalizeProcessCategoryMapForRow(Object.assign({}, row, {
|
|
processOrderCodes: validOrderCodes
|
|
}))
|
|
const processCategoryNameMap = {}
|
|
this.getProcessCategoryOptionsByBuNo(buNo).forEach(item => {
|
|
const categoryCode = item && item.categoryCode ? String(item.categoryCode).trim() : ''
|
|
const categoryName = item && item.categoryName ? String(item.categoryName).trim() : ''
|
|
if (!categoryCode || !categoryName) {
|
|
return
|
|
}
|
|
processCategoryNameMap[categoryCode] = categoryName
|
|
processCategoryNameMap[categoryCode.toLowerCase()] = categoryName
|
|
})
|
|
const showAll = validCodes.length >= validOrderCodes.length
|
|
nextMap[buNo] = {
|
|
showAll: showAll,
|
|
processOrderCodes: validOrderCodes,
|
|
processCodes: showAll ? validOrderCodes.slice() : validCodes,
|
|
processCategoryMap: processCategoryMap,
|
|
processCategoryNameMap: processCategoryNameMap
|
|
}
|
|
submitRows.push({
|
|
buNo: buNo,
|
|
showAll: showAll,
|
|
processCodes: validCodes,
|
|
processOrderCodes: validOrderCodes,
|
|
processCategoryMap: processCategoryMap
|
|
})
|
|
}
|
|
const inData = {
|
|
site: this.$store.state.user.site,
|
|
updateBy: this.$store.state.user.name,
|
|
rows: submitRows
|
|
}
|
|
saveProofTrackingBuProcessConfig(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
this.$message.error((data && data.msg) || '事业部进度展示设置保存失败')
|
|
return
|
|
}
|
|
this.buProcessConfigMap = nextMap
|
|
this.ensureBuProcessConfigRows()
|
|
this.$message.success(data.msg || '事业部进度展示设置已保存')
|
|
// 保存后重新拉取工序列和列表,确保页面立即按最新可见性与顺序刷新。
|
|
this.queryProcessColumns().then(() => {
|
|
this.getDataList()
|
|
this.refreshTrackingTableLayout()
|
|
})
|
|
}).catch(() => {
|
|
this.$message.error('事业部进度展示设置保存异常')
|
|
})
|
|
},
|
|
getRoleApi (apiKey) {
|
|
const apiMap = {
|
|
0: searchBusinessInfo,
|
|
1: searchBusinessInfo1,
|
|
2: searchBusinessInfo2,
|
|
3: searchBusinessInfo3,
|
|
4: searchBusinessInfo4,
|
|
5: searchBusinessInfo5,
|
|
6: searchBusinessInfo6,
|
|
7: searchBusinessInfo7,
|
|
8: searchBusinessInfo8
|
|
}
|
|
return apiMap[apiKey]
|
|
},
|
|
fetchRoleListByApiKey (apiKey, queryData) {
|
|
const apiFn = this.getRoleApi(apiKey)
|
|
if (!apiFn) {
|
|
return Promise.resolve([])
|
|
}
|
|
const params = {
|
|
site: this.$store.state.user.site,
|
|
username: queryData && queryData.username ? queryData.username : '',
|
|
userDisplay: queryData && queryData.userDisplay ? queryData.userDisplay : '',
|
|
roleDesc: '',
|
|
active: queryData && queryData.active ? queryData.active : '',
|
|
page: 1,
|
|
limit: 200
|
|
}
|
|
return apiFn(params).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
return (data.rows || []).map(item => {
|
|
return {
|
|
username: item.username,
|
|
userDisplay: item.userDisplay || item.user_display || '',
|
|
active: item.active
|
|
}
|
|
})
|
|
}
|
|
return []
|
|
})
|
|
},
|
|
newCustomer () {
|
|
this.newCustomerData.customerDesc = ''
|
|
this.newCustomerFlag = true
|
|
},
|
|
getBaseList (val) {
|
|
this.tagNo = val
|
|
this.$nextTick(() => {
|
|
const strVal = val === 509 ? (this.oneKeyForm.customerNo || '') : ''
|
|
this.$refs.baseList.init(val, strVal, '')
|
|
})
|
|
},
|
|
getBaseData (val) {
|
|
if (this.tagNo === 509) {
|
|
this.oneKeyForm.customerNo = val.customer_no
|
|
this.oneKeyForm.customerDesc = val.customer_desc
|
|
}
|
|
},
|
|
customerNoBlur () {
|
|
if (!this.oneKeyForm.customerNo) {
|
|
this.oneKeyForm.customerDesc = ''
|
|
return
|
|
}
|
|
const params = {
|
|
customerNo: this.oneKeyForm.customerNo,
|
|
createBy: this.$store.state.user.name
|
|
}
|
|
queryCustomerList(params).then(({ data }) => {
|
|
if (data && data.code === 0 && data.rows && data.rows.length === 1) {
|
|
this.oneKeyForm.customerDesc = data.rows[0].customerDesc
|
|
} else {
|
|
this.oneKeyForm.customerDesc = ''
|
|
}
|
|
})
|
|
},
|
|
saveNewCustomer () {
|
|
if (!this.newCustomerData.customerDesc) {
|
|
this.$message.warning('请输入客户名称!')
|
|
return
|
|
}
|
|
this.generateNextCustomerNo().then(customerNo => {
|
|
if (!customerNo) {
|
|
this.$alert('该客户已存在,请重新输入客户名称!', '提示', {
|
|
confirmButtonText: '确定',
|
|
type: 'warning'
|
|
})
|
|
return
|
|
}
|
|
const params = {
|
|
site: this.$store.state.user.site,
|
|
customerNo: customerNo,
|
|
customerDesc: this.newCustomerData.customerDesc,
|
|
createBy: this.$store.state.user.name
|
|
}
|
|
saveNewCustomer(params).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.oneKeyForm.customerNo = customerNo
|
|
this.oneKeyForm.customerDesc = this.newCustomerData.customerDesc
|
|
this.newCustomerFlag = false
|
|
this.$message.success(data.msg || '新增客户成功')
|
|
} else {
|
|
this.$alert(data && data.msg ? data.msg : '新增客户失败', '提示', {
|
|
confirmButtonText: '确定',
|
|
type: 'warning'
|
|
})
|
|
}
|
|
})
|
|
})
|
|
},
|
|
generateNextCustomerNo () {
|
|
const params = {
|
|
site: this.$store.state.user.site,
|
|
customerDesc: this.newCustomerData.customerDesc,
|
|
createBy: this.$store.state.user.name
|
|
}
|
|
return new Promise(resolve => {
|
|
getCustomerNo(params).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
resolve(false)
|
|
return
|
|
}
|
|
if (!data.data || !data.data.customerNo || data.data === '0') {
|
|
resolve('C0001')
|
|
return
|
|
}
|
|
const lastCustomerNo = parseInt(String(data.data.customerNo).substring(1), 10) || 0
|
|
const nextCustomerNo = lastCustomerNo + 1
|
|
if (nextCustomerNo < 10) {
|
|
resolve('C000' + nextCustomerNo)
|
|
} else if (nextCustomerNo < 100) {
|
|
resolve('C00' + nextCustomerNo)
|
|
} else if (nextCustomerNo < 1000) {
|
|
resolve('C0' + nextCustomerNo)
|
|
} else {
|
|
resolve('C' + nextCustomerNo)
|
|
}
|
|
}).catch(() => {
|
|
resolve(false)
|
|
})
|
|
})
|
|
},
|
|
closeNewCustomer () {
|
|
this.newCustomerData.customerDesc = ''
|
|
this.newCustomerFlag = false
|
|
},
|
|
openRoleDialog (field) {
|
|
const roleCfg = this.roleConfig[field]
|
|
if (!roleCfg) {
|
|
return
|
|
}
|
|
this.roleDialogField = field
|
|
this.roleDialogApiKey = roleCfg.apiKey
|
|
this.roleDialogTitle = roleCfg.title
|
|
this.roleSearchData = {
|
|
site: this.$store.state.user.site,
|
|
username: '',
|
|
userDisplay: '',
|
|
active: '',
|
|
page: 1,
|
|
limit: 50
|
|
}
|
|
this.roleDialogVisible = true
|
|
this.queryRoleDialogList()
|
|
},
|
|
queryRoleDialogList () {
|
|
this.fetchRoleListByApiKey(this.roleDialogApiKey, this.roleSearchData).then(rows => {
|
|
this.roleDialogList = rows
|
|
})
|
|
},
|
|
pickRole (row) {
|
|
const roleCfg = this.roleConfig[this.roleDialogField]
|
|
if (!roleCfg) {
|
|
return
|
|
}
|
|
const value = row.username + '-' + row.userDisplay
|
|
this.oneKeyForm[this.roleDialogField] = value
|
|
this.oneKeyForm[roleCfg.nameField] = row.userDisplay
|
|
this.roleDialogVisible = false
|
|
},
|
|
getEmptyOneKeyForm () {
|
|
return {
|
|
site: this.$store.state.user.site,
|
|
buNo: '',
|
|
projectNo: '',
|
|
projectName: '',
|
|
projectDesc: '',
|
|
projectStatus: '草稿',
|
|
projectSource: '',
|
|
testPartNo: '',
|
|
partDesc: '',
|
|
partName: '',
|
|
partSpec: '',
|
|
materialNumber: '',
|
|
finalPartDesc: '',
|
|
finalPartNo: '',
|
|
baseNo: '',
|
|
revNo: '',
|
|
customerNo: '',
|
|
customerDesc: '',
|
|
finalCustomerId: '',
|
|
customerRemark: '',
|
|
parentProjectNo: '',
|
|
oriProjectId: '',
|
|
projectCategory: '',
|
|
cProjectRegion: '',
|
|
projectManager: '',
|
|
projectManagerName: '',
|
|
projectOwner: '',
|
|
projectOwnerName: '',
|
|
cQualityEngineer1: '',
|
|
cQualityEngineer1Name: '',
|
|
cQualityEngineer2: '',
|
|
cQualityEngineer2Name: '',
|
|
cQualityEngineer3: '',
|
|
cQualityEngineer3Name: '',
|
|
cQualityEngineer4: '',
|
|
cQualityEngineer4Name: '',
|
|
cQualityEngineer5: '',
|
|
cQualityEngineer5Name: '',
|
|
cQualityEngineer6: '',
|
|
cQualityEngineer6Name: '',
|
|
cManufactureEngineer: '',
|
|
cManufactureEngineerName: '',
|
|
docEngineer: '',
|
|
docEngineerName: '',
|
|
docEngineer2: '',
|
|
docEngineer2Name: '',
|
|
ipqcHardTag: '',
|
|
ipqcHardTagName: '',
|
|
cQualityEngineer7: '',
|
|
cQualityEngineer7Name: '',
|
|
partType: '',
|
|
dry_wet_type: '',
|
|
partStatus: '草稿',
|
|
projectPhase: '',
|
|
tracker: '',
|
|
engineer: '',
|
|
engineerName: '',
|
|
priorityLevel: '',
|
|
proofingNo: '',
|
|
proofingStatus: '草稿',
|
|
proofingNumber: '',
|
|
projectCreationDate: '',
|
|
projectCloseDate: '',
|
|
buildDate: '',
|
|
closeDate: '',
|
|
comments: '',
|
|
planStartDate: '',
|
|
requiredDeliveryDate: '',
|
|
baseline: '',
|
|
needDate: '',
|
|
remark: ''
|
|
}
|
|
},
|
|
getDefaultProofDialogData () {
|
|
return {
|
|
trackingId: null,
|
|
site: this.$store.state.user.site,
|
|
projectId: null,
|
|
projectNo: '',
|
|
projectDesc: '',
|
|
buNo: '',
|
|
customerNo: '',
|
|
customerDesc: '',
|
|
projectPartId: null,
|
|
testPartNo: '',
|
|
partDesc: '',
|
|
projectCategory: '',
|
|
cProjectTypeDb: '',
|
|
projectPhase: '',
|
|
projectManager: '',
|
|
projectOwner: '',
|
|
engineer: '',
|
|
priorityLevel: '',
|
|
proofingNo: '',
|
|
proofingNumber: '',
|
|
planStartDate: '',
|
|
requiredDeliveryDate: '',
|
|
actualityDeliveryDate: '',
|
|
proofingStatus: '草稿',
|
|
remark: '',
|
|
createBy: this.$store.state.user.name,
|
|
updateBy: this.$store.state.user.name
|
|
}
|
|
},
|
|
getDataList (flag) {
|
|
if (flag === 'Y') {
|
|
this.pageIndex = 1
|
|
}
|
|
this.searchData.site = this.$store.state.user.site
|
|
this.searchData.userName = this.$store.state.user.name
|
|
const inData = Object.assign({}, this.searchData, {
|
|
page: this.pageIndex,
|
|
limit: this.pageSize
|
|
})
|
|
inData.buNo = this.normalizeBuNo(inData.buNo, inData.site)
|
|
this.dataListLoading = true
|
|
searchProofTracking(inData).then(({ data }) => {
|
|
this.dataListLoading = false
|
|
this.resetProcessTooltip()
|
|
if (data && data.code === 0) {
|
|
this.dataList = (data.page && data.page.list) || []
|
|
this.syncDataListProcessValues()
|
|
this.totalPage = (data.page && data.page.totalCount) || 0
|
|
if (this.commentsEditingTrackingId) {
|
|
const hasEditingRow = this.dataList.some(item => item.trackingId === this.commentsEditingTrackingId)
|
|
if (!hasEditingRow) {
|
|
this.cancelCommentsEdit()
|
|
}
|
|
}
|
|
const manualSelectedId = this.manualSelectedTrackingId
|
|
const previousCurrentId = this.currentRow && this.currentRow.trackingId
|
|
let nextCurrentRow = null
|
|
if (manualSelectedId != null) {
|
|
// 只要用户没有手动点新行,刷新后始终尝试回选上次手动选中的那一行
|
|
nextCurrentRow = this.dataList.find(item => item && item.trackingId === manualSelectedId) || null
|
|
} else if (previousCurrentId != null) {
|
|
nextCurrentRow = this.dataList.find(item => item && item.trackingId === previousCurrentId) || null
|
|
}
|
|
if (!nextCurrentRow && manualSelectedId == null && this.dataList.length > 0) {
|
|
nextCurrentRow = this.dataList[0]
|
|
}
|
|
this.currentRow = nextCurrentRow
|
|
this.$nextTick(() => {
|
|
if (this.$refs.trackingTable && typeof this.$refs.trackingTable.setCurrentRow === 'function') {
|
|
this.$refs.trackingTable.setCurrentRow(this.currentRow || null)
|
|
}
|
|
})
|
|
} else {
|
|
this.dataList = []
|
|
this.totalPage = 0
|
|
this.currentRow = null
|
|
this.$nextTick(() => {
|
|
if (this.$refs.trackingTable && typeof this.$refs.trackingTable.setCurrentRow === 'function') {
|
|
this.$refs.trackingTable.setCurrentRow(null)
|
|
}
|
|
})
|
|
this.$message.error(data.msg || '查询失败')
|
|
}
|
|
}).catch(() => {
|
|
this.dataListLoading = false
|
|
this.resetProcessTooltip()
|
|
this.$message.error('查询异常')
|
|
})
|
|
},
|
|
async exportTrackingData () {
|
|
if (this.exportLoading) {
|
|
return
|
|
}
|
|
this.searchData.site = this.$store.state.user.site
|
|
this.searchData.userName = this.$store.state.user.name
|
|
const exportTrackingColumns = this.visibleTrackingColumns.map(item => ({
|
|
columnProp: item.columnProp,
|
|
columnLabel: item.columnLabel
|
|
}))
|
|
const exportProcessColumns = this.visibleProcessColumns.map(item => ({
|
|
code: item.code,
|
|
label: item.label,
|
|
planField: item.planField,
|
|
actualField: item.actualField,
|
|
statusField: item.statusField,
|
|
processCategoryCode: item.processCategoryCode,
|
|
processCategoryName: item.processCategoryName
|
|
}))
|
|
if (exportTrackingColumns.length === 0 && exportProcessColumns.length === 0) {
|
|
this.$message.warning('当前没有可导出的列')
|
|
return
|
|
}
|
|
const inData = Object.assign({}, this.searchData, {
|
|
buNo: this.normalizeBuNo(this.searchData.buNo, this.searchData.site),
|
|
exportTrackingColumns: exportTrackingColumns,
|
|
exportProcessColumns: exportProcessColumns,
|
|
exportCommentsColumn: true
|
|
})
|
|
this.exportLoading = true
|
|
try {
|
|
const response = await exportProofTracking(inData)
|
|
const blob = response && response.data ? response.data : null
|
|
if (!blob) {
|
|
this.$message.error('导出失败')
|
|
return
|
|
}
|
|
if (blob.type && blob.type.indexOf('application/json') > -1) {
|
|
const errorText = await blob.text()
|
|
let errorMsg = '导出失败'
|
|
try {
|
|
const errorObj = JSON.parse(errorText)
|
|
if (errorObj && errorObj.msg) {
|
|
errorMsg = errorObj.msg
|
|
}
|
|
} catch (e) {
|
|
}
|
|
this.$message.error(errorMsg)
|
|
return
|
|
}
|
|
const fileName = this.resolveProofTrackingExportFileName(response)
|
|
const url = window.URL.createObjectURL(blob)
|
|
const link = document.createElement('a')
|
|
link.href = url
|
|
link.setAttribute('download', fileName)
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
link.remove()
|
|
window.URL.revokeObjectURL(url)
|
|
} catch (e) {
|
|
this.$message.error('导出失败')
|
|
} finally {
|
|
this.exportLoading = false
|
|
}
|
|
},
|
|
resolveProofTrackingExportFileName (response) {
|
|
const defaultFileName = `项目打样跟踪_${this.getExportTimestamp()}.xlsx`
|
|
const disposition = response && response.headers
|
|
? (response.headers['content-disposition'] || response.headers['Content-Disposition'] || '')
|
|
: ''
|
|
if (!disposition) {
|
|
return defaultFileName
|
|
}
|
|
const utf8Match = disposition.match(/filename\*=utf-8''([^;]+)/i)
|
|
if (utf8Match && utf8Match[1]) {
|
|
try {
|
|
return decodeURIComponent(utf8Match[1])
|
|
} catch (e) {
|
|
return utf8Match[1]
|
|
}
|
|
}
|
|
const normalMatch = disposition.match(/filename="?([^";]+)"?/i)
|
|
if (normalMatch && normalMatch[1]) {
|
|
return normalMatch[1]
|
|
}
|
|
return defaultFileName
|
|
},
|
|
getExportTimestamp () {
|
|
const now = new Date()
|
|
const pad = num => String(num).padStart(2, '0')
|
|
return `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`
|
|
},
|
|
resetSearch () {
|
|
this.searchData.projectNo = ''
|
|
this.searchData.projectDesc = ''
|
|
this.searchData.testPartNo = ''
|
|
this.searchData.partDesc = ''
|
|
this.searchData.dryWetType = ''
|
|
this.searchData.customerNo = ''
|
|
this.searchData.proofingNo = ''
|
|
this.searchData.projectPartSyncFlag = ''
|
|
this.searchData.proofSyncFlag = ''
|
|
this.searchData.proofingStatus = ''
|
|
this.ensureSearchBuNoSelected()
|
|
this.queryProcessColumns().then(() => {
|
|
this.getDataList('Y')
|
|
})
|
|
},
|
|
selectionChange (rows) {
|
|
this.selectionRows = rows || []
|
|
},
|
|
isCommentsEditing (row) {
|
|
return !!row && row.trackingId === this.commentsEditingTrackingId
|
|
},
|
|
startCommentsEdit (row) {
|
|
if (this.batchEditMode || !row || !row.trackingId || this.commentsSaveLoading) {
|
|
return
|
|
}
|
|
this.commentsEditingTrackingId = row.trackingId
|
|
this.commentsDraft = row.comments || ''
|
|
},
|
|
cancelCommentsEdit () {
|
|
if (this.commentsSaveLoading) {
|
|
return
|
|
}
|
|
this.commentsEditingTrackingId = null
|
|
this.commentsDraft = ''
|
|
},
|
|
handleCommentsBlur (row) {
|
|
if (this.commentsSaveLoading || !this.isCommentsEditing(row)) {
|
|
return
|
|
}
|
|
const comments = this.commentsDraft == null ? '' : String(this.commentsDraft)
|
|
if (comments.trim()) {
|
|
this.saveCommentsEdit(row, {
|
|
fromBlur: true,
|
|
draftComments: comments
|
|
})
|
|
return
|
|
}
|
|
this.cancelCommentsEdit()
|
|
},
|
|
saveCommentsEdit (row, options) {
|
|
const saveOptions = options || {}
|
|
const fromBlur = !!saveOptions.fromBlur
|
|
if (this.commentsSaveLoading) {
|
|
return
|
|
}
|
|
if (!row || !row.trackingId) {
|
|
this.$message.warning('跟踪记录ID不能为空')
|
|
return
|
|
}
|
|
const comments = Object.prototype.hasOwnProperty.call(saveOptions, 'draftComments')
|
|
? (saveOptions.draftComments == null ? '' : String(saveOptions.draftComments))
|
|
: (this.commentsDraft == null ? '' : String(this.commentsDraft))
|
|
if (comments === (row.comments || '')) {
|
|
this.cancelCommentsEdit()
|
|
return
|
|
}
|
|
if (fromBlur) {
|
|
this.commentsEditingTrackingId = null
|
|
this.commentsDraft = ''
|
|
}
|
|
this.commentsSaveLoading = true
|
|
updateProofTrackingComments({
|
|
trackingId: row.trackingId,
|
|
comments: comments,
|
|
updateBy: this.$store.state.user.name
|
|
}).then(({ data }) => {
|
|
this.commentsSaveLoading = false
|
|
if (data && data.code === 0) {
|
|
row.comments = comments
|
|
this.$message.success('Comments保存成功')
|
|
this.cancelCommentsEdit()
|
|
} else {
|
|
this.$message.error(data.msg || 'Comments保存失败')
|
|
}
|
|
}).catch(() => {
|
|
this.commentsSaveLoading = false
|
|
this.$message.error('Comments保存异常')
|
|
})
|
|
},
|
|
rowClick (row) {
|
|
this.currentRow = row
|
|
this.manualSelectedTrackingId = row && row.trackingId ? row.trackingId : null
|
|
},
|
|
sizeChangeHandle (val) {
|
|
this.pageSize = val
|
|
this.pageIndex = 1
|
|
this.getDataList()
|
|
},
|
|
currentChangeHandle (val) {
|
|
this.pageIndex = val
|
|
this.getDataList()
|
|
},
|
|
getActionRow (row) {
|
|
return row || this.currentRow || (this.selectionRows.length > 0 ? this.selectionRows[0] : null)
|
|
},
|
|
getTrackingProjectPartId (row) {
|
|
if (!row) {
|
|
return null
|
|
}
|
|
return row.projectPartId || row.project_part_id || null
|
|
},
|
|
isSyncedByIdRule (idValue) {
|
|
if (idValue == null || String(idValue).trim() === '') {
|
|
return false
|
|
}
|
|
// 约定:NPI 正式库主键 < 100000,tracking 临时主键 >= 100000。
|
|
const numericId = Number(idValue)
|
|
return Number.isFinite(numericId) && numericId > 0 && numericId < 100000
|
|
},
|
|
isProjectPartSynced (row) {
|
|
if (!row) {
|
|
return false
|
|
}
|
|
const projectId = row.projectId || row.project_id || null
|
|
const projectPartId = this.getTrackingProjectPartId(row)
|
|
return this.isSyncedByIdRule(projectId) && this.isSyncedByIdRule(projectPartId)
|
|
},
|
|
isProofSynced (row) {
|
|
if (!row) {
|
|
return false
|
|
}
|
|
const proofingId = row.proofingId || row.proofing_id || row.trackingId || null
|
|
return this.isSyncedByIdRule(proofingId)
|
|
},
|
|
getProjectPartSyncTextClass (row) {
|
|
return this.isProjectPartSynced(row) ? 'sync-text--ok' : 'sync-text--pending'
|
|
},
|
|
getProofSyncTextClass (row) {
|
|
return this.isProofSynced(row) ? 'sync-text--ok' : 'sync-text--pending'
|
|
},
|
|
getProjectCategoryValue (source) {
|
|
if (!source) {
|
|
return ''
|
|
}
|
|
const category = source.cProjectTypeDb || source.projectCategory || source.project_category || source.c_project_type_db || ''
|
|
return this.isBlankValue(category) ? '' : String(category).trim()
|
|
},
|
|
isOneKeyCreateFieldVisible (fieldKey) {
|
|
if (this.oneKeyDialogMode !== 'create') {
|
|
return true
|
|
}
|
|
return this.oneKeyCreateVisibleFieldKeys.indexOf(fieldKey) > -1
|
|
},
|
|
isBlankValue (value) {
|
|
return value == null || String(value).trim() === ''
|
|
},
|
|
getApplyQuantityDisplayText (applyQuantity) {
|
|
if (this.isBlankValue(applyQuantity)) {
|
|
return '未维护'
|
|
}
|
|
return String(applyQuantity).trim()
|
|
},
|
|
normalizeProjectNo (projectNo) {
|
|
if (this.isBlankValue(projectNo)) {
|
|
return ''
|
|
}
|
|
return String(projectNo).trim()
|
|
},
|
|
queryApplyOptionsByProjectNo (projectNo) {
|
|
const normalizedProjectNo = this.normalizeProjectNo(projectNo)
|
|
if (!normalizedProjectNo) {
|
|
return Promise.resolve([])
|
|
}
|
|
return searchExpApplyList({
|
|
projectNo: normalizedProjectNo,
|
|
page: 1,
|
|
limit: 500
|
|
}).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
throw new Error((data && data.msg) || '查询试验单失败')
|
|
}
|
|
const pageList = (data.page && data.page.list) || []
|
|
const matchedProjectNo = normalizedProjectNo.toUpperCase()
|
|
const optionMap = {}
|
|
const optionRows = []
|
|
pageList.forEach(item => {
|
|
if (!item || this.isBlankValue(item.applyNo)) {
|
|
return
|
|
}
|
|
const applyStatus = this.isBlankValue(item.status) ? '' : String(item.status).trim()
|
|
// 业务要求:已完成的试验单不允许继续作为 tracking 打样单号使用。
|
|
if (applyStatus === '草稿' || applyStatus === '已完成') {
|
|
return
|
|
}
|
|
const rowProjectNo = this.normalizeProjectNo(item.projectNo).toUpperCase()
|
|
// searchExpApplyList 按模糊 project_no 查询,这里再次做等值过滤,确保与 tracking 项目编码一一对应。
|
|
if (rowProjectNo !== matchedProjectNo) {
|
|
return
|
|
}
|
|
const applyNo = String(item.applyNo).trim()
|
|
if (optionMap[applyNo]) {
|
|
return
|
|
}
|
|
optionMap[applyNo] = true
|
|
optionRows.push({
|
|
applyNo: applyNo,
|
|
expectedFinishDate: this.formatDate(item.expectedFinishDate),
|
|
applyQuantity: this.isBlankValue(item.quantityReq) ? '' : String(item.quantityReq).trim()
|
|
})
|
|
})
|
|
return optionRows
|
|
})
|
|
},
|
|
handleOneKeyProjectNoBlur () {
|
|
const projectNo = this.normalizeProjectNo(this.oneKeyForm.projectNo)
|
|
const previousProjectNo = this.normalizeProjectNo(this.oneKeyProjectNoSnapshot)
|
|
this.oneKeyForm.projectNo = projectNo
|
|
if (!projectNo) {
|
|
this.oneKeyProofingApplyQueryToken += 1
|
|
this.oneKeyProofingApplyLoading = false
|
|
this.oneKeyProofingApplyOptions = []
|
|
this.oneKeyForm.proofingNo = ''
|
|
this.oneKeyForm.requiredDeliveryDate = ''
|
|
this.oneKeyProjectNoSnapshot = ''
|
|
this.oneKeyForm.testPartNo = ''
|
|
this.oneKeyForm.partDesc = ''
|
|
this.oneKeyForm.partType = ''
|
|
this.oneKeyForm.dry_wet_type = ''
|
|
return
|
|
}
|
|
if (previousProjectNo.toUpperCase() !== projectNo.toUpperCase()) {
|
|
// 项目编码变化后需清理旧料号,避免跨项目保留旧料号与角色信息。
|
|
this.oneKeyForm.testPartNo = ''
|
|
this.oneKeyForm.partDesc = ''
|
|
this.oneKeyForm.partType = ''
|
|
this.oneKeyForm.dry_wet_type = ''
|
|
}
|
|
this.oneKeyProjectNoSnapshot = projectNo
|
|
this.loadOneKeyProofingApplyOptions(projectNo, { keepCurrentIfMissing: false })
|
|
},
|
|
handleOneKeyProofingNoChange (proofingNo) {
|
|
const selectedNo = this.isBlankValue(proofingNo) ? '' : String(proofingNo).trim()
|
|
if (this.oneKeyDialogMode === 'edit') {
|
|
// 修改模式下预计完成日期以数据库已存值为准,不跟随打样单号自动回填/清空。
|
|
if (!selectedNo) {
|
|
this.oneKeyForm.proofingNo = ''
|
|
return
|
|
}
|
|
const matchedInEdit = this.oneKeyProofingApplyOptions.find(item => item && item.applyNo === selectedNo)
|
|
if (matchedInEdit) {
|
|
this.oneKeyForm.proofingNo = matchedInEdit.applyNo
|
|
}
|
|
return
|
|
}
|
|
if (!selectedNo) {
|
|
this.oneKeyForm.requiredDeliveryDate = ''
|
|
return
|
|
}
|
|
const matched = this.oneKeyProofingApplyOptions.find(item => item && item.applyNo === selectedNo)
|
|
if (!matched) {
|
|
return
|
|
}
|
|
this.oneKeyForm.proofingNo = matched.applyNo
|
|
this.oneKeyForm.requiredDeliveryDate = matched.expectedFinishDate || ''
|
|
this.syncOneKeyBaselineByRequiredDeliveryDate(this.oneKeyForm.requiredDeliveryDate, false)
|
|
},
|
|
handleOneKeyRequiredDeliveryDateChange (dateVal) {
|
|
this.syncOneKeyBaselineByRequiredDeliveryDate(dateVal, false)
|
|
},
|
|
syncOneKeyBaselineByRequiredDeliveryDate (requiredDeliveryDate, forceSync) {
|
|
const normalizedRequiredDate = this.formatDate(requiredDeliveryDate)
|
|
if (!normalizedRequiredDate) {
|
|
return
|
|
}
|
|
const normalizedBaseline = this.formatDate(this.oneKeyForm.baseline)
|
|
if (forceSync || !normalizedBaseline) {
|
|
// baseline 首次默认跟随 Delivery&Package 日期,后续可按业务独立调整。
|
|
this.oneKeyForm.baseline = normalizedRequiredDate
|
|
}
|
|
},
|
|
loadOneKeyProofingApplyOptions (projectNo, options) {
|
|
const queryOptions = options || {}
|
|
const normalizedProjectNo = this.normalizeProjectNo(projectNo)
|
|
const currentProofingNo = this.isBlankValue(this.oneKeyForm.proofingNo)
|
|
? ''
|
|
: String(this.oneKeyForm.proofingNo).trim()
|
|
const queryToken = this.oneKeyProofingApplyQueryToken + 1
|
|
this.oneKeyProofingApplyQueryToken = queryToken
|
|
if (!normalizedProjectNo) {
|
|
this.oneKeyProofingApplyLoading = false
|
|
this.oneKeyProofingApplyOptions = []
|
|
return Promise.resolve([])
|
|
}
|
|
this.oneKeyProofingApplyLoading = true
|
|
return this.queryApplyOptionsByProjectNo(normalizedProjectNo).then(optionRows => {
|
|
if (queryToken !== this.oneKeyProofingApplyQueryToken) {
|
|
return optionRows
|
|
}
|
|
const nextOptions = optionRows.slice()
|
|
const keepCurrentIfMissing = !!queryOptions.keepCurrentIfMissing
|
|
if (currentProofingNo) {
|
|
const hasCurrent = nextOptions.some(item => item && item.applyNo === currentProofingNo)
|
|
if (!hasCurrent && keepCurrentIfMissing) {
|
|
// 历史记录可能早于 ERF 规范化,保留原打样单号避免编辑场景被强制清空。
|
|
nextOptions.unshift({
|
|
applyNo: currentProofingNo,
|
|
expectedFinishDate: this.formatDate(this.oneKeyForm.requiredDeliveryDate),
|
|
applyQuantity: ''
|
|
})
|
|
}
|
|
if (!hasCurrent && !keepCurrentIfMissing) {
|
|
this.oneKeyForm.proofingNo = ''
|
|
this.oneKeyForm.requiredDeliveryDate = ''
|
|
}
|
|
}
|
|
this.oneKeyProofingApplyOptions = nextOptions
|
|
if (currentProofingNo) {
|
|
this.handleOneKeyProofingNoChange(currentProofingNo)
|
|
}
|
|
return nextOptions
|
|
}).catch((e) => {
|
|
if (queryToken === this.oneKeyProofingApplyQueryToken) {
|
|
this.oneKeyProofingApplyOptions = []
|
|
this.$message.error((e && e.message) || '查询试验单异常')
|
|
}
|
|
return []
|
|
}).finally(() => {
|
|
if (queryToken === this.oneKeyProofingApplyQueryToken) {
|
|
this.oneKeyProofingApplyLoading = false
|
|
}
|
|
})
|
|
},
|
|
handleProofDialogProofingNoChange (proofingNo) {
|
|
const selectedNo = this.isBlankValue(proofingNo) ? '' : String(proofingNo).trim()
|
|
if (!selectedNo) {
|
|
this.proofDialogData.requiredDeliveryDate = ''
|
|
return
|
|
}
|
|
const matched = this.proofDialogApplyOptions.find(item => item && item.applyNo === selectedNo)
|
|
if (!matched) {
|
|
return
|
|
}
|
|
this.proofDialogData.proofingNo = matched.applyNo
|
|
this.proofDialogData.requiredDeliveryDate = matched.expectedFinishDate || ''
|
|
},
|
|
loadProofDialogApplyOptions (projectNo) {
|
|
const normalizedProjectNo = this.normalizeProjectNo(projectNo)
|
|
const queryToken = this.proofDialogApplyQueryToken + 1
|
|
this.proofDialogApplyQueryToken = queryToken
|
|
if (!normalizedProjectNo) {
|
|
this.proofDialogApplyLoading = false
|
|
this.proofDialogApplyOptions = []
|
|
this.proofDialogData.proofingNo = ''
|
|
this.proofDialogData.requiredDeliveryDate = ''
|
|
return Promise.resolve([])
|
|
}
|
|
this.proofDialogApplyLoading = true
|
|
return this.queryApplyOptionsByProjectNo(normalizedProjectNo).then(optionRows => {
|
|
if (queryToken !== this.proofDialogApplyQueryToken) {
|
|
return optionRows
|
|
}
|
|
this.proofDialogApplyOptions = optionRows
|
|
const currentProofingNo = this.isBlankValue(this.proofDialogData.proofingNo)
|
|
? ''
|
|
: String(this.proofDialogData.proofingNo).trim()
|
|
if (currentProofingNo) {
|
|
this.handleProofDialogProofingNoChange(currentProofingNo)
|
|
}
|
|
return optionRows
|
|
}).catch((e) => {
|
|
if (queryToken === this.proofDialogApplyQueryToken) {
|
|
this.proofDialogApplyOptions = []
|
|
this.$message.error((e && e.message) || '查询试验单异常')
|
|
}
|
|
return []
|
|
}).finally(() => {
|
|
if (queryToken === this.proofDialogApplyQueryToken) {
|
|
this.proofDialogApplyLoading = false
|
|
}
|
|
})
|
|
},
|
|
getBuOptionValue (site, buNo) {
|
|
const rawBuNo = buNo == null ? '' : String(buNo).trim()
|
|
if (!rawBuNo) {
|
|
return ''
|
|
}
|
|
if (rawBuNo.indexOf('_') > -1) {
|
|
return rawBuNo
|
|
}
|
|
const targetSuffix = `_${rawBuNo}`
|
|
const matched = this.userBuList.find(item => item && item.buNo && (item.buNo === rawBuNo || item.buNo === `${site}_${rawBuNo}` || String(item.buNo).endsWith(targetSuffix)))
|
|
if (matched && matched.buNo) {
|
|
return matched.buNo
|
|
}
|
|
return site ? `${site}_${rawBuNo}` : rawBuNo
|
|
},
|
|
getRoleDisplayName (value) {
|
|
if (this.isBlankValue(value)) {
|
|
return ''
|
|
}
|
|
const strVal = String(value)
|
|
const splitIdx = strVal.indexOf('-')
|
|
return splitIdx > -1 ? strVal.substring(splitIdx + 1) : strVal
|
|
},
|
|
normalizeRoleValue (value) {
|
|
if (this.isBlankValue(value)) {
|
|
return ''
|
|
}
|
|
return String(value).trim()
|
|
},
|
|
getSiteByJoinedBuNo (buNo) {
|
|
const rawBuNo = buNo == null ? '' : String(buNo).trim()
|
|
if (!rawBuNo) {
|
|
return this.$store.state.user.site
|
|
}
|
|
const splitIdx = rawBuNo.indexOf('_')
|
|
if (splitIdx > 0) {
|
|
return rawBuNo.substring(0, splitIdx)
|
|
}
|
|
return this.$store.state.user.site
|
|
},
|
|
getOneKeyBuQueryContext () {
|
|
const currentBuNo = this.oneKeyForm && this.oneKeyForm.buNo
|
|
? String(this.oneKeyForm.buNo).trim()
|
|
: ''
|
|
const normalizedBuNo = this.normalizeBuNo(currentBuNo, this.$store.state.user.site)
|
|
if (!normalizedBuNo) {
|
|
return null
|
|
}
|
|
const matchedBu = this.userBuList.find(item => {
|
|
if (!item || !item.buNo) {
|
|
return false
|
|
}
|
|
const itemBuNo = this.normalizeBuNo(item.buNo, this.$store.state.user.site)
|
|
return itemBuNo.toUpperCase() === normalizedBuNo.toUpperCase()
|
|
}) || null
|
|
const joinedBuNo = matchedBu && matchedBu.buNo ? matchedBu.buNo : currentBuNo
|
|
return {
|
|
normalizedBuNo: normalizedBuNo,
|
|
buDesc: matchedBu && matchedBu.buDesc ? String(matchedBu.buDesc).trim() : '',
|
|
site: this.getSiteByJoinedBuNo(joinedBuNo)
|
|
}
|
|
},
|
|
isOneKeyDryWetTypeVisible () {
|
|
return this.isRfidBuNo(this.oneKeyForm && this.oneKeyForm.buNo ? this.oneKeyForm.buNo : '')
|
|
},
|
|
handleOneKeyBuChange () {
|
|
// 干湿分类仅用于 RFID,切换到其他 BU 时主动清空,避免隐藏字段误提交。
|
|
if (!this.isOneKeyDryWetTypeVisible()) {
|
|
this.oneKeyForm.dry_wet_type = ''
|
|
}
|
|
},
|
|
getRowDateSortTime (row, fieldList) {
|
|
if (!row || !Array.isArray(fieldList)) {
|
|
return 0
|
|
}
|
|
for (let i = 0; i < fieldList.length; i += 1) {
|
|
const fieldKey = fieldList[i]
|
|
const rawVal = row[fieldKey]
|
|
if (this.isBlankValue(rawVal)) {
|
|
continue
|
|
}
|
|
const date = new Date(rawVal)
|
|
if (isNaN(date.getTime())) {
|
|
continue
|
|
}
|
|
return date.getTime()
|
|
}
|
|
return 0
|
|
},
|
|
sortRowsByDateDesc (rows, fieldList) {
|
|
const sourceRows = Array.isArray(rows) ? rows : []
|
|
return sourceRows.slice().sort((a, b) => {
|
|
const diff = this.getRowDateSortTime(b, fieldList) - this.getRowDateSortTime(a, fieldList)
|
|
if (diff !== 0) {
|
|
return diff
|
|
}
|
|
const leftKey = this.isBlankValue(a && a.projectNo) ? (a && a.testPartNo ? String(a.testPartNo) : '') : String(a.projectNo)
|
|
const rightKey = this.isBlankValue(b && b.projectNo) ? (b && b.testPartNo ? String(b.testPartNo) : '') : String(b.projectNo)
|
|
return leftKey.localeCompare(rightKey)
|
|
})
|
|
},
|
|
queryOneKeyProjectOptions (projectNoKeyword) {
|
|
const buContext = this.getOneKeyBuQueryContext()
|
|
if (!buContext) {
|
|
return Promise.resolve([])
|
|
}
|
|
const queryKeyword = this.isBlankValue(projectNoKeyword) ? '' : String(projectNoKeyword).trim()
|
|
const inData = {
|
|
site: buContext.site,
|
|
userName: this.$store.state.user.name,
|
|
buDesc: buContext.buDesc,
|
|
projectNo: queryKeyword,
|
|
projectDesc: '',
|
|
customerNo: '',
|
|
customerDesc: '',
|
|
projectCategory: '',
|
|
status: '',
|
|
projectManager: '',
|
|
projectOwner: '',
|
|
engineer: '',
|
|
cProjectRegion: '',
|
|
startDate: '',
|
|
endDate: '',
|
|
page: 1,
|
|
limit: 1000
|
|
}
|
|
return eamProjectInfoSearch(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
return []
|
|
}
|
|
const sourceRows = (data.page && data.page.list) || []
|
|
const targetBuNo = buContext.normalizedBuNo.toUpperCase()
|
|
const targetBuDesc = this.isBlankValue(buContext.buDesc) ? '' : String(buContext.buDesc).trim().toUpperCase()
|
|
const normalizedKeyword = queryKeyword.toUpperCase()
|
|
const filteredRows = sourceRows.filter(item => {
|
|
if (!item || this.isBlankValue(item.projectNo)) {
|
|
return false
|
|
}
|
|
const rowBuNo = this.normalizeBuNo(item.buNo || item.bu || '', buContext.site).toUpperCase()
|
|
const rowBuDesc = this.isBlankValue(item.buDesc) ? '' : String(item.buDesc).trim().toUpperCase()
|
|
const matchedBu = (targetBuNo && rowBuNo && rowBuNo === targetBuNo) || (targetBuDesc && rowBuDesc && rowBuDesc === targetBuDesc)
|
|
const hasRowBuInfo = !this.isBlankValue(item.buNo || item.bu || item.buDesc)
|
|
if (hasRowBuInfo && !matchedBu) {
|
|
return false
|
|
}
|
|
if (!normalizedKeyword) {
|
|
return true
|
|
}
|
|
return String(item.projectNo).toUpperCase().indexOf(normalizedKeyword) > -1
|
|
})
|
|
return this.sortRowsByDateDesc(filteredRows, ['createDate', 'projectCreationDate', 'updateDate'])
|
|
}).catch(() => {
|
|
return []
|
|
})
|
|
},
|
|
queryOneKeyPartOptions (projectNo, partNoKeyword) {
|
|
const normalizedProjectNo = this.normalizeProjectNo(projectNo)
|
|
if (!normalizedProjectNo) {
|
|
return Promise.resolve([])
|
|
}
|
|
const buContext = this.getOneKeyBuQueryContext()
|
|
const queryKeyword = this.isBlankValue(partNoKeyword) ? '' : String(partNoKeyword).trim()
|
|
const inData = {
|
|
site: buContext ? buContext.site : this.$store.state.user.site,
|
|
userName: this.$store.state.user.name,
|
|
buDesc: buContext ? buContext.buDesc : '',
|
|
projectNo: normalizedProjectNo,
|
|
projectDesc: '',
|
|
testPartNo: queryKeyword,
|
|
partDesc: '',
|
|
customerNo: '',
|
|
customerDesc: '',
|
|
projectCategory: '',
|
|
status: '',
|
|
startDate: '',
|
|
endDate: '',
|
|
projectManager: '',
|
|
projectOwner: '',
|
|
engineer: '',
|
|
cProjectRegion: '',
|
|
finalPartNo: '',
|
|
massProductionStartDate: '',
|
|
massProductionEndDate: '',
|
|
feedingFlag: '0',
|
|
page: 1,
|
|
limit: 1000
|
|
}
|
|
return eamProjectPartSearch(inData).then(({ data }) => {
|
|
if (!(data && data.code === 0)) {
|
|
return []
|
|
}
|
|
const sourceRows = (data.page && data.page.list) || []
|
|
const targetProjectNo = normalizedProjectNo.toUpperCase()
|
|
const normalizedKeyword = queryKeyword.toUpperCase()
|
|
const filteredRows = sourceRows.filter(item => {
|
|
const rowPartNo = item && item.testPartNo ? String(item.testPartNo).trim() : ''
|
|
const rowProjectNo = this.normalizeProjectNo(item && item.projectNo ? item.projectNo : '').toUpperCase()
|
|
if (!rowPartNo || rowProjectNo !== targetProjectNo) {
|
|
return false
|
|
}
|
|
if (!normalizedKeyword) {
|
|
return true
|
|
}
|
|
return rowPartNo.toUpperCase().indexOf(normalizedKeyword) > -1
|
|
})
|
|
return this.sortRowsByDateDesc(filteredRows, ['createDate', 'updateDate', 'buildDate'])
|
|
}).catch(() => {
|
|
return []
|
|
})
|
|
},
|
|
queryOneKeyProjectSuggestions (queryString, callback) {
|
|
const queryToken = this.oneKeyProjectQueryToken + 1
|
|
this.oneKeyProjectQueryToken = queryToken
|
|
this.queryOneKeyProjectOptions(queryString).then(rows => {
|
|
if (queryToken !== this.oneKeyProjectQueryToken) {
|
|
return
|
|
}
|
|
const suggestionRows = rows.map(item => {
|
|
const projectNo = this.normalizeProjectNo(item.projectNo)
|
|
if (!projectNo) {
|
|
return null
|
|
}
|
|
return {
|
|
value: projectNo,
|
|
projectDesc: item.projectDesc || item.projectName || '',
|
|
customerDesc: item.customerDesc || '',
|
|
createDateText: this.formatDate(item.createDate || item.projectCreationDate || item.updateDate),
|
|
source: item
|
|
}
|
|
}).filter(item => !!item)
|
|
callback(suggestionRows)
|
|
}).catch(() => {
|
|
if (queryToken === this.oneKeyProjectQueryToken) {
|
|
callback([])
|
|
}
|
|
})
|
|
},
|
|
queryOneKeyPartSuggestions (queryString, callback) {
|
|
const projectNo = this.normalizeProjectNo(this.oneKeyForm.projectNo)
|
|
if (!projectNo) {
|
|
callback([])
|
|
return
|
|
}
|
|
const queryToken = this.oneKeyPartQueryToken + 1
|
|
this.oneKeyPartQueryToken = queryToken
|
|
this.queryOneKeyPartOptions(projectNo, queryString).then(rows => {
|
|
if (queryToken !== this.oneKeyPartQueryToken) {
|
|
return
|
|
}
|
|
const suggestionRows = rows.map(item => {
|
|
const partNo = item && item.testPartNo ? String(item.testPartNo).trim() : ''
|
|
if (!partNo) {
|
|
return null
|
|
}
|
|
return {
|
|
value: partNo,
|
|
partDesc: item.partDesc || '',
|
|
createDateText: this.formatDate(item.createDate || item.updateDate || item.buildDate),
|
|
source: item
|
|
}
|
|
}).filter(item => !!item)
|
|
callback(suggestionRows)
|
|
}).catch(() => {
|
|
if (queryToken === this.oneKeyPartQueryToken) {
|
|
callback([])
|
|
}
|
|
})
|
|
},
|
|
setOneKeyRoleFieldValue (fieldKey, roleValue) {
|
|
const normalizedRoleValue = this.normalizeRoleValue(roleValue)
|
|
this.oneKeyForm[fieldKey] = normalizedRoleValue
|
|
const roleCfg = this.roleConfig[fieldKey]
|
|
if (roleCfg && roleCfg.nameField) {
|
|
this.oneKeyForm[roleCfg.nameField] = this.getRoleDisplayName(normalizedRoleValue)
|
|
}
|
|
},
|
|
setOneKeyRoleFieldsFromSource (source) {
|
|
const roleSource = source || {}
|
|
ONE_KEY_ROLE_FIELDS.forEach(fieldKey => {
|
|
this.setOneKeyRoleFieldValue(fieldKey, roleSource[fieldKey])
|
|
})
|
|
},
|
|
snapshotOneKeyProjectRoles (source) {
|
|
const roleSource = source || {}
|
|
const roleBaseline = {}
|
|
ONE_KEY_ROLE_FIELDS.forEach(fieldKey => {
|
|
roleBaseline[fieldKey] = this.normalizeRoleValue(roleSource[fieldKey])
|
|
})
|
|
this.oneKeyProjectRoleBaseline = roleBaseline
|
|
},
|
|
syncOneKeyPartRoleFields (partRow) {
|
|
const changedRoleFields = []
|
|
ONE_KEY_ROLE_FIELDS.forEach(fieldKey => {
|
|
const partRoleValue = this.normalizeRoleValue(partRow && partRow[fieldKey])
|
|
if (!partRoleValue) {
|
|
return
|
|
}
|
|
const baselineRoleValue = this.normalizeRoleValue(this.oneKeyProjectRoleBaseline[fieldKey])
|
|
if (baselineRoleValue === partRoleValue) {
|
|
return
|
|
}
|
|
const currentRoleValue = this.normalizeRoleValue(this.oneKeyForm[fieldKey])
|
|
if (currentRoleValue === partRoleValue) {
|
|
return
|
|
}
|
|
this.setOneKeyRoleFieldValue(fieldKey, partRoleValue)
|
|
changedRoleFields.push(fieldKey)
|
|
})
|
|
if (changedRoleFields.length > 0) {
|
|
this.$message.info('项目料号角色人员有变动,已同步角色账号')
|
|
}
|
|
},
|
|
applyOneKeyProjectSelection (projectRow) {
|
|
const selectedProject = projectRow || {}
|
|
const projectNo = this.normalizeProjectNo(selectedProject.projectNo)
|
|
if (!projectNo) {
|
|
return
|
|
}
|
|
const previousProjectNo = this.normalizeProjectNo(this.oneKeyProjectNoSnapshot)
|
|
const projectChanged = previousProjectNo.toUpperCase() !== projectNo.toUpperCase()
|
|
this.oneKeyForm.projectNo = projectNo
|
|
this.oneKeyProjectNoSnapshot = projectNo
|
|
this.oneKeyForm.projectName = selectedProject.projectName || selectedProject.projectDesc || ''
|
|
this.oneKeyForm.projectDesc = selectedProject.projectDesc || selectedProject.projectName || ''
|
|
this.oneKeyForm.customerNo = this.isBlankValue(selectedProject.customerNo) ? '' : String(selectedProject.customerNo).trim()
|
|
this.oneKeyForm.customerDesc = selectedProject.customerDesc || ''
|
|
this.oneKeyForm.projectCreationDate = this.formatDate(selectedProject.projectCreationDate || selectedProject.createDate)
|
|
this.oneKeyForm.needDate = this.formatDate(selectedProject.needDate)
|
|
this.oneKeyForm.projectCategory = this.getProjectCategoryValue(selectedProject)
|
|
this.oneKeyForm.cProjectRegion = this.isBlankValue(selectedProject.cProjectRegion) ? '' : String(selectedProject.cProjectRegion).trim()
|
|
const rawPriority = this.isBlankValue(selectedProject.priorityLevel) ? selectedProject.priority : selectedProject.priorityLevel
|
|
this.oneKeyForm.priorityLevel = this.isBlankValue(rawPriority) ? '' : String(rawPriority).trim()
|
|
this.setOneKeyRoleFieldsFromSource(selectedProject)
|
|
this.snapshotOneKeyProjectRoles(selectedProject)
|
|
if (projectChanged) {
|
|
// 项目切换时清理料号相关字段,防止旧料号角色残留到新项目。
|
|
this.oneKeyForm.testPartNo = ''
|
|
this.oneKeyForm.partDesc = ''
|
|
this.oneKeyForm.partType = ''
|
|
this.oneKeyForm.dry_wet_type = ''
|
|
}
|
|
this.loadOneKeyProofingApplyOptions(projectNo, { keepCurrentIfMissing: false })
|
|
},
|
|
applyOneKeyPartSelection (partRow) {
|
|
const selectedPart = partRow || {}
|
|
const partNo = this.isBlankValue(selectedPart.testPartNo) ? '' : String(selectedPart.testPartNo).trim()
|
|
if (!partNo) {
|
|
return
|
|
}
|
|
this.oneKeyForm.testPartNo = partNo
|
|
if (!this.isBlankValue(selectedPart.partDesc)) {
|
|
this.oneKeyForm.partDesc = String(selectedPart.partDesc).trim()
|
|
}
|
|
if (!this.isBlankValue(selectedPart.partType)) {
|
|
this.oneKeyForm.partType = String(selectedPart.partType).trim()
|
|
}
|
|
// 接口可能返回 camelCase 或下划线字段名,这里统一兼容后回填到表单。
|
|
const selectedDryWetType = this.isBlankValue(selectedPart.dry_wet_type)
|
|
? selectedPart.dryWetType
|
|
: selectedPart.dry_wet_type
|
|
if (this.isOneKeyDryWetTypeVisible() && !this.isBlankValue(selectedDryWetType)) {
|
|
this.oneKeyForm.dry_wet_type = String(selectedDryWetType).trim()
|
|
} else if (!this.isOneKeyDryWetTypeVisible()) {
|
|
this.oneKeyForm.dry_wet_type = ''
|
|
}
|
|
// 业务要求:料号角色与项目角色存在差异时,界面需优先回填料号角色账号。
|
|
this.syncOneKeyPartRoleFields(selectedPart)
|
|
},
|
|
handleOneKeyProjectSelect (item) {
|
|
const selectedRow = item && item.source ? item.source : null
|
|
if (!selectedRow) {
|
|
return
|
|
}
|
|
this.applyOneKeyProjectSelection(selectedRow)
|
|
},
|
|
handleOneKeyPartSelect (item) {
|
|
const selectedRow = item && item.source ? item.source : null
|
|
if (!selectedRow) {
|
|
return
|
|
}
|
|
this.applyOneKeyPartSelection(selectedRow)
|
|
},
|
|
handleOneKeyPartNoBlur () {
|
|
this.oneKeyForm.testPartNo = this.isBlankValue(this.oneKeyForm.testPartNo)
|
|
? ''
|
|
: String(this.oneKeyForm.testPartNo).trim()
|
|
},
|
|
buildOneKeySubmitPayload () {
|
|
const inData = Object.assign({}, this.oneKeyForm, {
|
|
createBy: this.$store.state.user.name,
|
|
updateBy: this.$store.state.user.name
|
|
})
|
|
if (!inData.projectName && inData.projectDesc) {
|
|
inData.projectName = inData.projectDesc
|
|
}
|
|
if (!inData.projectDesc && inData.projectName) {
|
|
inData.projectDesc = inData.projectName
|
|
}
|
|
if (!inData.tracker) {
|
|
inData.tracker = inData.projectOwner
|
|
}
|
|
if (!inData.priorityLevel) {
|
|
inData.priorityLevel = ''
|
|
}
|
|
if (this.oneKeyDialogMode === 'create' && this.isBlankValue(inData.baseline) && !this.isBlankValue(inData.requiredDeliveryDate)) {
|
|
// 一键创建若已带出 Delivery&Package 日期,则 baseline 默认同步该日期(允许用户提交前改写)。
|
|
inData.baseline = this.formatDate(inData.requiredDeliveryDate)
|
|
}
|
|
// 干湿分类仅在 RFID 使用,非 RFID 提交时统一清空,避免旧值串到错误 BU。
|
|
if (!this.isRfidBuNo(inData.buNo)) {
|
|
inData.dry_wet_type = ''
|
|
}
|
|
// 弹窗仅展示一个立项日期输入,提交时同步到项目与物料两个日期字段,避免物料立项日期被误判为空
|
|
if (!this.isBlankValue(inData.projectCreationDate)) {
|
|
inData.buildDate = inData.projectCreationDate
|
|
} else if (!this.isBlankValue(inData.buildDate)) {
|
|
inData.projectCreationDate = inData.buildDate
|
|
}
|
|
return inData
|
|
},
|
|
validateOneKeySubmitPayload (inData) {
|
|
if (this.isBlankValue(inData.site) || this.isBlankValue(inData.buNo)) {
|
|
this.$message.error('工厂和BU不能为空')
|
|
return false
|
|
}
|
|
// create 模式在保存前要求补齐关键责任人与料号描述,避免创建后出现主责缺失数据。
|
|
if (this.oneKeyDialogMode === 'create') {
|
|
if (this.isOneKeyCreateFieldVisible('partDesc') && this.isBlankValue(inData.partDesc)) {
|
|
this.$message.error('料号描述不能为空')
|
|
return false
|
|
}
|
|
if (this.isOneKeyCreateFieldVisible('projectOwner') && this.isBlankValue(inData.projectOwner)) {
|
|
this.$message.error('PjM不能为空')
|
|
return false
|
|
}
|
|
if (this.isOneKeyCreateFieldVisible('engineer') && this.isBlankValue(inData.engineer)) {
|
|
this.$message.error('Engineer不能为空')
|
|
return false
|
|
}
|
|
}
|
|
if (this.oneKeyDialogMode === 'edit') {
|
|
if (!inData.projectId || !inData.projectPartId || !inData.trackingId) {
|
|
this.$message.error('项目/物料/打样记录ID缺失,无法修改')
|
|
return false
|
|
}
|
|
// 修改模式按业务要求仅保留 3 个必填项,其他业务字段允许为空。
|
|
if (this.isBlankValue(inData.partDesc)) {
|
|
this.$message.error('料号描述不能为空')
|
|
return false
|
|
}
|
|
if (this.isBlankValue(inData.projectOwner)) {
|
|
this.$message.error('PjM不能为空')
|
|
return false
|
|
}
|
|
if (this.isBlankValue(inData.engineer)) {
|
|
this.$message.error('Engineer不能为空')
|
|
return false
|
|
}
|
|
if (!this.isBlankValue(inData.proofingNumber) && !/^[1-9]\d*$/.test(String(inData.proofingNumber))) {
|
|
this.$message.error('打样数量必须是正整数')
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
},
|
|
buildOneKeyEditForm (current, projectData, partData) {
|
|
const project = projectData || {}
|
|
const part = partData || {}
|
|
const form = Object.assign(this.getEmptyOneKeyForm(), {
|
|
site: project.site || part.site || current.site || this.$store.state.user.site,
|
|
projectId: project.projectId || current.projectId || null,
|
|
projectPartId: part.projectPartId || this.getTrackingProjectPartId(current),
|
|
trackingId: current.trackingId || null,
|
|
buNo: this.getBuOptionValue(project.site || current.site || this.$store.state.user.site, project.buNo || current.buNo),
|
|
projectNo: project.projectNo || current.projectNo || '',
|
|
projectName: project.projectName || project.projectDesc || current.projectName || current.projectDesc || '',
|
|
projectDesc: project.projectDesc || project.projectName || current.projectDesc || current.projectName || '',
|
|
projectStatus: project.status || project.projectStatus || '草稿',
|
|
projectSource: project.projectSource || '',
|
|
testPartNo: part.testPartNo || current.testPartNo || '',
|
|
partDesc: part.partDesc || current.partDesc || '',
|
|
partName: part.partName || '',
|
|
partSpec: part.partSpec || '',
|
|
materialNumber: part.materialNumber || '',
|
|
finalPartDesc: part.finalPartDesc || '',
|
|
finalPartNo: part.finalPartNo || current.finalPartNo || '',
|
|
baseNo: part.baseNo || '',
|
|
revNo: part.revNo || '',
|
|
customerNo: project.customerNo || part.customerNo || current.customerNo || '',
|
|
customerDesc: project.customerDesc || current.customerDesc || '',
|
|
finalCustomerId: project.finalCustomerId || '',
|
|
customerRemark: project.customerRemark || '',
|
|
parentProjectNo: project.parentProjectNo || '',
|
|
oriProjectId: project.oriProjectId || '',
|
|
projectCategory: project.projectCategory || part.projectCategory || current.projectCategory || '',
|
|
cProjectRegion: project.cProjectRegion || current.cProjectRegion || '',
|
|
projectManager: project.projectManager || part.projectManager || current.projectManager || '',
|
|
projectOwner: project.projectOwner || part.projectOwner || current.projectOwner || '',
|
|
cQualityEngineer1: project.cQualityEngineer1 || part.cQualityEngineer1 || '',
|
|
cQualityEngineer2: project.cQualityEngineer2 || part.cQualityEngineer2 || '',
|
|
cQualityEngineer3: project.cQualityEngineer3 || part.cQualityEngineer3 || '',
|
|
cQualityEngineer4: project.cQualityEngineer4 || part.cQualityEngineer4 || '',
|
|
cQualityEngineer5: project.cQualityEngineer5 || part.cQualityEngineer5 || '',
|
|
cQualityEngineer6: project.cQualityEngineer6 || part.cQualityEngineer6 || '',
|
|
cManufactureEngineer: project.cManufactureEngineer || part.cManufactureEngineer || '',
|
|
docEngineer: project.docEngineer || part.docEngineer || '',
|
|
docEngineer2: project.docEngineer2 || part.docEngineer2 || '',
|
|
ipqcHardTag: project.ipqcHardTag || part.ipqcHardTag || '',
|
|
cQualityEngineer7: project.cQualityEngineer7 || part.cQualityEngineer7 || '',
|
|
partType: part.partType || '',
|
|
dry_wet_type: part.dry_wet_type || part.dryWetType || current.dry_wet_type || current.dryWetType || '',
|
|
partStatus: part.status || part.partStatus || '草稿',
|
|
projectPhase: current.projectPhase || '',
|
|
tracker: current.tracker || project.projectOwner || part.projectOwner || '',
|
|
engineer: current.engineer || project.engineer || part.engineer || '',
|
|
priorityLevel: current.priorityLevel || current.priority || project.priority || part.priority || '',
|
|
proofingNo: current.proofingNo || '',
|
|
proofingStatus: current.proofingStatus || '进行中',
|
|
proofingNumber: current.proofingNumber,
|
|
projectCreationDate: project.projectCreationDate || part.buildDate || '',
|
|
projectCloseDate: project.projectCloseDate || '',
|
|
buildDate: part.buildDate || project.projectCreationDate || '',
|
|
closeDate: part.closeDate || '',
|
|
comments: current.comments || '',
|
|
planStartDate: current.planStartDate || '',
|
|
requiredDeliveryDate: current.requiredDeliveryDate || '',
|
|
baseline: current.baseline || '',
|
|
needDate: project.needDate || part.needDate || current.needDate || '',
|
|
remark: part.remark || project.remark || current.remark || ''
|
|
})
|
|
form.projectManagerName = this.getRoleDisplayName(form.projectManager)
|
|
form.projectOwnerName = this.getRoleDisplayName(form.projectOwner)
|
|
form.engineerName = this.getRoleDisplayName(form.engineer)
|
|
form.cManufactureEngineerName = this.getRoleDisplayName(form.cManufactureEngineer)
|
|
form.cQualityEngineer1Name = this.getRoleDisplayName(form.cQualityEngineer1)
|
|
form.cQualityEngineer2Name = this.getRoleDisplayName(form.cQualityEngineer2)
|
|
form.cQualityEngineer3Name = this.getRoleDisplayName(form.cQualityEngineer3)
|
|
form.cQualityEngineer4Name = this.getRoleDisplayName(form.cQualityEngineer4)
|
|
form.cQualityEngineer5Name = this.getRoleDisplayName(form.cQualityEngineer5)
|
|
form.cQualityEngineer6Name = this.getRoleDisplayName(form.cQualityEngineer6)
|
|
form.docEngineerName = this.getRoleDisplayName(form.docEngineer)
|
|
form.docEngineer2Name = this.getRoleDisplayName(form.docEngineer2)
|
|
form.ipqcHardTagName = this.getRoleDisplayName(form.ipqcHardTag)
|
|
form.cQualityEngineer7Name = this.getRoleDisplayName(form.cQualityEngineer7)
|
|
if (!this.isRfidBuNo(form.buNo)) {
|
|
form.dry_wet_type = ''
|
|
}
|
|
return form
|
|
},
|
|
openOneKeyDialog (mode, row) {
|
|
const dialogMode = mode === 'edit' ? 'edit' : 'create'
|
|
this.oneKeyDialogMode = dialogMode
|
|
if (dialogMode !== 'edit') {
|
|
this.oneKeyProofingApplyQueryToken += 1
|
|
this.oneKeyProofingApplyLoading = false
|
|
this.oneKeyProofingApplyOptions = []
|
|
this.oneKeyProjectQueryToken += 1
|
|
this.oneKeyPartQueryToken += 1
|
|
this.oneKeyProjectNoSnapshot = ''
|
|
this.oneKeyProjectRoleBaseline = {}
|
|
this.oneKeyForm = this.getEmptyOneKeyForm()
|
|
const selectedSearchBuNo = this.getSelectedSearchBuNo()
|
|
if (selectedSearchBuNo) {
|
|
// 一键创建默认沿用查询区 BU,避免用户重复选择。
|
|
this.oneKeyForm.buNo = this.getBuOptionValue(this.searchData.site, selectedSearchBuNo)
|
|
} else if (this.userBuList.length > 0) {
|
|
this.oneKeyForm.buNo = this.userBuList[0].buNo || ''
|
|
}
|
|
this.handleOneKeyBuChange()
|
|
this.oneKeyDialogVisible = true
|
|
return
|
|
}
|
|
const current = this.getActionRow(row)
|
|
if (!current) {
|
|
this.$message.warning('请先选择一条记录后再修改项目/物料/打样')
|
|
this.oneKeyDialogMode = 'create'
|
|
return
|
|
}
|
|
const projectPartId = this.getTrackingProjectPartId(current)
|
|
if (!current.projectId || !projectPartId || !current.trackingId) {
|
|
this.$message.warning('当前记录缺少项目/物料/打样关键信息,无法修改')
|
|
this.oneKeyDialogMode = 'create'
|
|
return
|
|
}
|
|
const queryUser = this.$store.state.user.name
|
|
const querySite = current.site || this.$store.state.user.site
|
|
this.saveOneKeyLoading = true
|
|
Promise.all([
|
|
searchProjectInfoTracking({
|
|
site: querySite,
|
|
userName: queryUser,
|
|
projectId: current.projectId,
|
|
page: 1,
|
|
limit: 1
|
|
}),
|
|
searchProjectPartTracking({
|
|
site: querySite,
|
|
userName: queryUser,
|
|
projectPartId: projectPartId,
|
|
page: 1,
|
|
limit: 1
|
|
})
|
|
]).then(([projectResp, partResp]) => {
|
|
const projectData = projectResp && projectResp.data
|
|
const partData = partResp && partResp.data
|
|
if (!projectData || projectData.code !== 0) {
|
|
throw new Error((projectData && projectData.msg) || '加载项目信息失败')
|
|
}
|
|
if (!partData || partData.code !== 0) {
|
|
throw new Error((partData && partData.msg) || '加载项目物料信息失败')
|
|
}
|
|
const projectRows = (projectData.page && projectData.page.list) || []
|
|
const partRows = (partData.page && partData.page.list) || []
|
|
if (projectRows.length === 0) {
|
|
throw new Error('未找到对应的项目信息,无法修改')
|
|
}
|
|
if (partRows.length === 0) {
|
|
throw new Error('未找到对应的项目物料信息,无法修改')
|
|
}
|
|
this.oneKeyForm = this.buildOneKeyEditForm(current, projectRows[0], partRows[0])
|
|
this.snapshotOneKeyProjectRoles(projectRows[0])
|
|
this.oneKeyProjectNoSnapshot = this.normalizeProjectNo(this.oneKeyForm.projectNo)
|
|
this.oneKeyProofingApplyQueryToken += 1
|
|
this.oneKeyProofingApplyLoading = false
|
|
this.oneKeyProofingApplyOptions = []
|
|
this.oneKeyProjectQueryToken += 1
|
|
this.oneKeyPartQueryToken += 1
|
|
this.oneKeyDialogVisible = true
|
|
this.loadOneKeyProofingApplyOptions(this.oneKeyForm.projectNo, { keepCurrentIfMissing: true })
|
|
}).catch((e) => {
|
|
this.$message.error((e && e.message) || '加载项目/物料/打样信息异常')
|
|
this.oneKeyDialogMode = 'create'
|
|
}).finally(() => {
|
|
this.saveOneKeyLoading = false
|
|
})
|
|
},
|
|
submitOneKey () {
|
|
const inData = this.buildOneKeySubmitPayload()
|
|
if (!this.validateOneKeySubmitPayload(inData)) {
|
|
return
|
|
}
|
|
this.saveOneKeyLoading = true
|
|
if (this.oneKeyDialogMode === 'edit') {
|
|
oneKeyUpdateProofTracking(inData).then(({ data }) => {
|
|
if (!data || data.code !== 0) {
|
|
throw new Error((data && data.msg) || '修改项目/物料/打样失败')
|
|
}
|
|
this.$message.success('项目/物料/打样修改成功')
|
|
this.oneKeyDialogVisible = false
|
|
this.getDataList()
|
|
}).catch((e) => {
|
|
this.$message.error((e && e.message) || '项目/物料/打样修改异常')
|
|
}).finally(() => {
|
|
this.saveOneKeyLoading = false
|
|
})
|
|
return
|
|
}
|
|
oneKeyCreateProofTracking(inData).then(({ data }) => {
|
|
this.saveOneKeyLoading = false
|
|
if (data && data.code === 0) {
|
|
this.$message.success('一键创建成功')
|
|
this.oneKeyDialogVisible = false
|
|
this.searchData.projectNo = ''
|
|
this.searchData.projectDesc = ''
|
|
this.searchData.testPartNo = ''
|
|
this.searchData.partDesc = ''
|
|
this.searchData.dryWetType = ''
|
|
this.searchData.customerNo = ''
|
|
this.searchData.proofingNo = ''
|
|
this.searchData.projectPartSyncFlag = ''
|
|
this.searchData.proofSyncFlag = ''
|
|
this.searchData.proofingStatus = ''
|
|
this.getDataList('Y')
|
|
} else {
|
|
this.$message.error(data.msg || '一键创建失败')
|
|
}
|
|
}).catch(() => {
|
|
this.saveOneKeyLoading = false
|
|
this.$message.error('一键创建异常')
|
|
})
|
|
},
|
|
openCreateProofDialog (row) {
|
|
const current = this.getActionRow(row)
|
|
if (!current) {
|
|
this.$message.warning('请先选择一条记录')
|
|
return
|
|
}
|
|
this.currentRow = current
|
|
const projectPartId = this.getTrackingProjectPartId(current) || current.id
|
|
const projectCategory = this.getProjectCategoryValue(current)
|
|
this.proofDialogApplyQueryToken += 1
|
|
this.proofDialogApplyLoading = false
|
|
this.proofDialogApplyOptions = []
|
|
this.proofDialogData = Object.assign(this.getDefaultProofDialogData(), {
|
|
trackingId: null,
|
|
site: current.site || this.$store.state.user.site,
|
|
projectId: current.projectId,
|
|
projectNo: current.projectNo,
|
|
projectDesc: current.projectDesc,
|
|
buNo: current.buNo,
|
|
customerNo: current.customerNo,
|
|
customerDesc: current.customerDesc,
|
|
projectPartId: projectPartId,
|
|
testPartNo: current.testPartNo,
|
|
partDesc: current.partDesc,
|
|
projectCategory: projectCategory,
|
|
cProjectTypeDb: projectCategory,
|
|
projectManager: current.projectManager,
|
|
projectOwner: current.projectOwner,
|
|
engineer: current.engineer,
|
|
priorityLevel: current.priority,
|
|
proofingNo: '',
|
|
proofingNumber: '',
|
|
planStartDate: '',
|
|
requiredDeliveryDate: '',
|
|
actualityDeliveryDate: '',
|
|
proofingStatus: '草稿',
|
|
remark: '',
|
|
createBy: this.$store.state.user.name,
|
|
updateBy: this.$store.state.user.name
|
|
})
|
|
if (this.isBlankValue(projectCategory)) {
|
|
this.$message.warning('未获取到项目分类,请手动选择')
|
|
}
|
|
this.proofDialogVisible = true
|
|
this.loadProofDialogApplyOptions(this.proofDialogData.projectNo)
|
|
},
|
|
collectSyncToNpiMissingLabels (row) {
|
|
const projectPartId = this.getTrackingProjectPartId(row)
|
|
const missingLabels = []
|
|
if (this.isBlankValue(row && row.site)) {
|
|
missingLabels.push('工厂')
|
|
}
|
|
if (this.isBlankValue(row && row.buNo)) {
|
|
missingLabels.push('BU')
|
|
}
|
|
if (!row || !row.projectId) {
|
|
missingLabels.push('项目ID')
|
|
}
|
|
if (this.isBlankValue(row && row.projectNo)) {
|
|
missingLabels.push('项目编码')
|
|
}
|
|
if (this.isBlankValue(row && row.projectName) && this.isBlankValue(row && row.projectDesc)) {
|
|
missingLabels.push('项目名称')
|
|
}
|
|
if (!projectPartId) {
|
|
missingLabels.push('项目物料ID')
|
|
}
|
|
if (this.isBlankValue(row && row.testPartNo)) {
|
|
missingLabels.push('项目料号')
|
|
}
|
|
if (this.isBlankValue(row && row.partDesc)) {
|
|
missingLabels.push('料号描述')
|
|
}
|
|
return missingLabels
|
|
},
|
|
getSelectedRowsForBatchSync (actionLabel) {
|
|
const rows = this.selectionRows && this.selectionRows.length > 0 ? this.selectionRows : []
|
|
if (rows.length === 0) {
|
|
this.$message.warning(`请先勾选需要${actionLabel}的记录`)
|
|
}
|
|
return rows
|
|
},
|
|
syncBatchProjectPartToNpi () {
|
|
const selectedRows = this.getSelectedRowsForBatchSync('同步项目物料到NPI')
|
|
if (selectedRows.length === 0) {
|
|
return
|
|
}
|
|
const idMap = {}
|
|
const projectPartIds = []
|
|
const invalidRows = []
|
|
selectedRows.forEach(row => {
|
|
const projectPartId = this.getTrackingProjectPartId(row)
|
|
const missingLabels = this.collectSyncToNpiMissingLabels(row)
|
|
if (!projectPartId || missingLabels.length > 0) {
|
|
invalidRows.push({
|
|
projectNo: row && row.projectNo ? row.projectNo : '-',
|
|
testPartNo: row && row.testPartNo ? row.testPartNo : '-',
|
|
missingLabels: missingLabels.length > 0 ? missingLabels : ['项目物料ID']
|
|
})
|
|
return
|
|
}
|
|
const idKey = String(projectPartId)
|
|
if (idMap[idKey]) {
|
|
return
|
|
}
|
|
idMap[idKey] = true
|
|
projectPartIds.push(projectPartId)
|
|
})
|
|
if (projectPartIds.length === 0) {
|
|
this.$message.warning('勾选记录未包含可同步的项目物料')
|
|
return
|
|
}
|
|
const invalidHint = invalidRows.length > 0
|
|
? `<br/>其中${invalidRows.length}条记录不满足同步条件,已自动跳过`
|
|
: ''
|
|
const confirmMsg = `确认批量同步项目物料到NPI吗?<br/>已勾选${selectedRows.length}条,可同步${projectPartIds.length}条${invalidHint}`
|
|
this.$confirm(confirmMsg, '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
dangerouslyUseHTMLString: true
|
|
}).then(() => {
|
|
const operator = this.$store.state.user.name
|
|
this.syncBatchNpiLoading = true
|
|
batchSyncProjectPartTracking({
|
|
projectPartIds: projectPartIds,
|
|
updateBy: operator,
|
|
createBy: operator,
|
|
userName: operator
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const syncedCount = data.syncedCount || projectPartIds.length
|
|
this.$message.success(`批量同步成功,共${syncedCount}条项目物料`)
|
|
this.getDataList()
|
|
} else {
|
|
const failMsg = (data && data.msg) || '批量同步项目物料到NPI失败'
|
|
if (failMsg.indexOf('缺少以下必填项') > -1) {
|
|
this.$alert(failMsg, '同步校验未通过', {
|
|
confirmButtonText: '确定'
|
|
})
|
|
} else {
|
|
this.$message.error(failMsg)
|
|
}
|
|
}
|
|
}).catch(() => {
|
|
this.$message.error('批量同步项目物料到NPI异常')
|
|
}).finally(() => {
|
|
this.syncBatchNpiLoading = false
|
|
})
|
|
}).catch(() => {})
|
|
},
|
|
syncBatchProofToNpi () {
|
|
const selectedRows = this.getSelectedRowsForBatchSync('同步打样到NPI')
|
|
if (selectedRows.length === 0) {
|
|
return
|
|
}
|
|
const idMap = {}
|
|
const trackingIds = []
|
|
const invalidRows = []
|
|
selectedRows.forEach(row => {
|
|
const trackingId = row && row.trackingId
|
|
if (!trackingId) {
|
|
invalidRows.push({
|
|
projectNo: row && row.projectNo ? row.projectNo : '-',
|
|
testPartNo: row && row.testPartNo ? row.testPartNo : '-',
|
|
proofingNo: row && row.proofingNo ? row.proofingNo : '-'
|
|
})
|
|
return
|
|
}
|
|
const idKey = String(trackingId)
|
|
if (idMap[idKey]) {
|
|
return
|
|
}
|
|
idMap[idKey] = true
|
|
trackingIds.push(trackingId)
|
|
})
|
|
if (trackingIds.length === 0) {
|
|
this.$message.warning('勾选记录未包含可同步的打样单')
|
|
return
|
|
}
|
|
const invalidHint = invalidRows.length > 0
|
|
? `<br/>其中${invalidRows.length}条记录缺少打样ID,已自动跳过`
|
|
: ''
|
|
const confirmMsg = `确认批量同步打样到NPI吗?<br/>已勾选${selectedRows.length}条,可同步${trackingIds.length}条${invalidHint}`
|
|
this.$confirm(confirmMsg, '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning',
|
|
dangerouslyUseHTMLString: true
|
|
}).then(() => {
|
|
const operator = this.$store.state.user.name
|
|
this.syncBatchProofLoading = true
|
|
batchSyncProofTracking({
|
|
trackingIds: trackingIds,
|
|
updateBy: operator,
|
|
createBy: operator,
|
|
userName: operator
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
const syncedCount = data.syncedCount || trackingIds.length
|
|
this.$message.success(`批量同步成功,共${syncedCount}条打样记录`)
|
|
this.getDataList()
|
|
} else {
|
|
const failMsg = (data && data.msg) || '批量同步打样到NPI失败'
|
|
if (failMsg.indexOf('缺少以下必填项') > -1 || failMsg.indexOf('请先同步项目') > -1 || failMsg.indexOf('请先同步项目物料') > -1) {
|
|
this.$alert(failMsg, '同步校验未通过', {
|
|
confirmButtonText: '确定'
|
|
})
|
|
} else {
|
|
this.$message.error(failMsg)
|
|
}
|
|
}
|
|
}).catch(() => {
|
|
this.$message.error('批量同步打样到NPI异常')
|
|
}).finally(() => {
|
|
this.syncBatchProofLoading = false
|
|
})
|
|
}).catch(() => {})
|
|
},
|
|
validateProofDialogData () {
|
|
if (!this.proofDialogData.projectId || !this.proofDialogData.projectPartId) {
|
|
this.$message.warning('项目或项目料号信息不完整')
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.cProjectTypeDb) {
|
|
this.$message.warning('项目分类不能为空')
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.projectPhase) {
|
|
this.$message.warning('项目阶段不能为空')
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.proofingNo) {
|
|
this.$message.warning('打样单号不能为空')
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.proofingNumber) {
|
|
this.$message.warning('数量不能为空')
|
|
return false
|
|
}
|
|
if (!/^[1-9]\d*$/.test(String(this.proofDialogData.proofingNumber))) {
|
|
this.$alert('数量必需是大于等于0的正整数', '提示', {
|
|
confirmButtonText: '确定'
|
|
})
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.planStartDate) {
|
|
this.$message.warning('打样开始日期不能为空')
|
|
return false
|
|
}
|
|
if (!this.proofDialogData.requiredDeliveryDate) {
|
|
this.$message.warning('预计完成日期不能为空')
|
|
return false
|
|
}
|
|
return true
|
|
},
|
|
buildProofPayload () {
|
|
return {
|
|
site: this.proofDialogData.site,
|
|
projectId: this.proofDialogData.projectId,
|
|
projectPartId: this.proofDialogData.projectPartId,
|
|
projectNo: this.proofDialogData.projectNo,
|
|
testPartNo: this.proofDialogData.testPartNo,
|
|
customerNo: this.proofDialogData.customerNo,
|
|
buNo: this.proofDialogData.buNo,
|
|
projectCategory: this.proofDialogData.cProjectTypeDb,
|
|
projectPhase: this.proofDialogData.projectPhase,
|
|
proofingNo: this.proofDialogData.proofingNo,
|
|
proofingNumber: Number(this.proofDialogData.proofingNumber),
|
|
planStartDate: this.proofDialogData.planStartDate,
|
|
requiredDeliveryDate: this.proofDialogData.requiredDeliveryDate,
|
|
actualityDeliveryDate: this.proofDialogData.actualityDeliveryDate,
|
|
proofingStatus: this.proofDialogData.proofingStatus || '草稿',
|
|
remark: this.proofDialogData.remark,
|
|
tracker: this.proofDialogData.projectOwner,
|
|
engineer: this.proofDialogData.engineer,
|
|
priorityLevel: this.proofDialogData.priorityLevel,
|
|
createBy: this.$store.state.user.name,
|
|
updateBy: this.$store.state.user.name
|
|
}
|
|
},
|
|
saveProofRecord () {
|
|
if (!this.validateProofDialogData()) {
|
|
return
|
|
}
|
|
const payload = this.buildProofPayload()
|
|
this.proofSaveLoading = true
|
|
createProofTrackingRecord(payload).then(({ data }) => {
|
|
this.proofSaveLoading = false
|
|
if (data && data.code === 0) {
|
|
this.$message.success(data.msg || '新增打样成功')
|
|
this.proofDialogVisible = false
|
|
this.getDataList()
|
|
} else {
|
|
this.$alert(data && data.msg ? data.msg : '新增打样失败', '错误', { confirmButtonText: '确定' })
|
|
}
|
|
}).catch(() => {
|
|
this.proofSaveLoading = false
|
|
this.$message.error('新增打样异常')
|
|
})
|
|
},
|
|
toggleBatchEdit () {
|
|
this.batchEditMode = !this.batchEditMode
|
|
if (this.batchEditMode) {
|
|
this.cancelCommentsEdit()
|
|
}
|
|
this.$nextTick(() => {
|
|
const tableRef = this.$refs.trackingTable
|
|
if (tableRef && tableRef.doLayout) {
|
|
tableRef.doLayout()
|
|
}
|
|
if (this.batchEditMode) {
|
|
setTimeout(() => {
|
|
this.scrollToRawMaterialColumn()
|
|
}, 0)
|
|
}
|
|
})
|
|
},
|
|
scrollToRawMaterialColumn () {
|
|
const tableRef = this.$refs.trackingTable
|
|
if (!tableRef || !tableRef.$el) {
|
|
return
|
|
}
|
|
const headerWrapper = tableRef.headerWrapper || tableRef.$el.querySelector('.el-table__header-wrapper')
|
|
const bodyWrapper = tableRef.bodyWrapper || tableRef.$el.querySelector('.el-table__body-wrapper')
|
|
if (!headerWrapper || !bodyWrapper) {
|
|
return
|
|
}
|
|
const headerCells = headerWrapper.querySelectorAll('th')
|
|
let targetCell = null
|
|
for (let i = 0; i < headerCells.length; i++) {
|
|
const text = (headerCells[i].innerText || '').replace(/\s+/g, ' ').trim()
|
|
if (text === 'Raw Material') {
|
|
targetCell = headerCells[i]
|
|
break
|
|
}
|
|
}
|
|
if (!targetCell) {
|
|
return
|
|
}
|
|
const fixedLeftWrapper = tableRef.$el.querySelector('.el-table__fixed')
|
|
const fixedLeftWidth = fixedLeftWrapper ? fixedLeftWrapper.offsetWidth : 0
|
|
const nextScrollLeft = Math.max(targetCell.offsetLeft - fixedLeftWidth - 8, 0)
|
|
bodyWrapper.scrollLeft = nextScrollLeft
|
|
headerWrapper.scrollLeft = nextScrollLeft
|
|
},
|
|
saveBatchPlan () {
|
|
const rows = this.selectionRows && this.selectionRows.length > 0 ? this.selectionRows : this.dataList
|
|
if (!rows || rows.length === 0) {
|
|
this.$message.warning('没有可保存的数据')
|
|
return
|
|
}
|
|
const payloadRows = rows.map(row => ({
|
|
trackingId: row.trackingId,
|
|
rawMaterialActualDate: row.rawMaterialActualDate,
|
|
cylinderCutterActualDate: row.cylinderCutterActualDate,
|
|
laminationActualDate: row.laminationActualDate,
|
|
printingActualDate: row.printingActualDate,
|
|
etchingActualDate: row.etchingActualDate,
|
|
slittingPackingActualDate: row.slittingPackingActualDate,
|
|
bondingActualDate: row.bondingActualDate,
|
|
cl60Cl822ActualDate: row.cl60Cl822ActualDate,
|
|
spottingActualDate: row.spottingActualDate,
|
|
encodingActualDate: row.encodingActualDate,
|
|
dieCutActualDate: row.dieCutActualDate,
|
|
inspectionActualDate: row.inspectionActualDate,
|
|
deliveryPackageActualDate: row.deliveryPackageActualDate,
|
|
comments: row.comments == null ? '' : String(row.comments),
|
|
updateBy: this.$store.state.user.name
|
|
}))
|
|
batchUpdateProofTrackingPlan({
|
|
updateBy: this.$store.state.user.name,
|
|
rows: payloadRows
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('批量修改成功')
|
|
this.batchEditMode = false
|
|
this.getDataList()
|
|
} else {
|
|
this.$message.error(data.msg || '批量修改失败')
|
|
}
|
|
}).catch(() => {
|
|
this.$message.error('批量修改异常')
|
|
})
|
|
},
|
|
handleProcessDateChange (row, processCol, dateVal) {
|
|
if (!row || !processCol) {
|
|
return
|
|
}
|
|
const actualDate = this.formatDate(dateVal)
|
|
// 业务要求:表格选择日期仅更新日期,不自动改变工序状态(状态由“√”动作控制)。
|
|
const currentStatus = row[processCol.statusField] ? String(row[processCol.statusField]).trim() : ''
|
|
const nextStatus = currentStatus || '未完成'
|
|
this.updateProcessFromTable(row, processCol, {
|
|
status: nextStatus,
|
|
actualDate: actualDate,
|
|
remark: actualDate ? '表格直接修改实际完成日期(状态不变)' : '表格清空实际完成日期(状态不变)'
|
|
}, true)
|
|
},
|
|
markProcessComplete (row, processCol) {
|
|
if (!row || !processCol || this.isProcessStatusComplete(row, processCol)) {
|
|
return
|
|
}
|
|
// 若用户已手工选择日期,打勾仅改状态,不覆盖已选日期;无日期时才补当天。
|
|
const currentActualDate = this.formatDate(row[processCol.actualField])
|
|
const nextActualDate = currentActualDate || this.formatDate(new Date())
|
|
this.updateProcessFromTable(row, processCol, {
|
|
status: '已完成',
|
|
actualDate: nextActualDate,
|
|
remark: '表格一键完成'
|
|
}, false, '工序已标记为完成')
|
|
},
|
|
handleProcessDateDoubleClick (row, processCol) {
|
|
if (!row || !processCol || !this.isProcessStatusComplete(row, processCol)) {
|
|
return
|
|
}
|
|
// 按页面交互约定:已完成工序允许双击日期输入框进行回退。
|
|
this.rollbackProcessComplete(row, processCol)
|
|
},
|
|
rollbackProcessComplete (row, processCol) {
|
|
if (!row || !processCol || !this.isProcessStatusComplete(row, processCol)) {
|
|
return
|
|
}
|
|
const currentActualDate = this.formatDate(row[processCol.actualField])
|
|
this.$confirm('确定回退该工序状态为“未完成”吗?', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
// 仅回退状态,保留当前实际完成日期,避免误回退时丢失用户已维护的日期信息。
|
|
this.updateProcessFromTable(row, processCol, {
|
|
status: '未完成',
|
|
actualDate: currentActualDate,
|
|
remark: '表格一键回退为未完成'
|
|
}, false, '工序状态已回退为未完成')
|
|
}).catch(() => {})
|
|
},
|
|
updateProcessFromTable (row, processCol, processData, silentSuccess, successMsg) {
|
|
if (!row || !processCol || !row.trackingId) {
|
|
this.$message.error('工序参数缺失')
|
|
return
|
|
}
|
|
const status = processData && processData.status ? String(processData.status).trim() : ''
|
|
if (!status) {
|
|
this.$message.error('工序状态不能为空')
|
|
return
|
|
}
|
|
const actualDate = processData && processData.actualDate ? this.formatDate(processData.actualDate) : ''
|
|
const inData = {
|
|
trackingId: row.trackingId,
|
|
processCode: processCol.code,
|
|
processName: processCol.label,
|
|
status: status,
|
|
actualDate: actualDate || null,
|
|
remark: processData && processData.remark ? processData.remark : '',
|
|
updateBy: this.$store.state.user.name
|
|
}
|
|
updateProofTrackingProcess(inData).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.updateRowProcessValue(row, processCol, status, actualDate)
|
|
const cacheKey = `${row.trackingId}_${processCol.code}`
|
|
if (this.processHistoryMap[cacheKey]) {
|
|
this.$delete(this.processHistoryMap, cacheKey)
|
|
}
|
|
if (!silentSuccess) {
|
|
this.$message.success(successMsg || '工序更新成功')
|
|
}
|
|
// 按业务要求:工序日期或状态变更后回查列表,保证动态列/派生字段展示最新值。
|
|
this.getDataList()
|
|
} else {
|
|
this.$message.error(data.msg || '工序更新失败')
|
|
this.getDataList()
|
|
}
|
|
}).catch(() => {
|
|
this.$message.error('工序更新异常')
|
|
this.getDataList()
|
|
})
|
|
},
|
|
openProcessDialog (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return
|
|
}
|
|
this.currentRow = row
|
|
this.processForm = {
|
|
trackingId: row.trackingId,
|
|
processCode: processCol.code,
|
|
processName: processCol.label,
|
|
status: row[processCol.statusField] || '未完成',
|
|
actualDate: row[processCol.actualField] || '',
|
|
remark: ''
|
|
}
|
|
this.processHistoryRows = []
|
|
this.processDialogVisible = true
|
|
this.loadProcessHistory(row, processCol, true)
|
|
},
|
|
submitProcessUpdate () {
|
|
if (!this.processForm.trackingId || !this.processForm.processCode) {
|
|
this.$message.error('参数缺失')
|
|
return
|
|
}
|
|
if (!this.processForm.status) {
|
|
this.$message.error('请选择状态')
|
|
return
|
|
}
|
|
const inData = Object.assign({}, this.processForm, {
|
|
updateBy: this.$store.state.user.name
|
|
})
|
|
this.saveProcessLoading = true
|
|
updateProofTrackingProcess(inData).then(({ data }) => {
|
|
this.saveProcessLoading = false
|
|
if (data && data.code === 0) {
|
|
this.$message.success('工序更新成功')
|
|
this.processDialogVisible = false
|
|
this.processHistoryMap = {}
|
|
this.getDataList()
|
|
} else {
|
|
this.$message.error(data.msg || '工序更新失败')
|
|
}
|
|
}).catch(() => {
|
|
this.saveProcessLoading = false
|
|
this.$message.error('工序更新异常')
|
|
})
|
|
},
|
|
finishProof (row) {
|
|
const current = row || this.currentRow
|
|
if (!current || !current.trackingId) {
|
|
this.$message.warning('请先选择记录')
|
|
return
|
|
}
|
|
this.finishForm = {
|
|
trackingId: current.trackingId,
|
|
actualityDeliveryDate: current.actualityDeliveryDate || this.formatDate(new Date())
|
|
}
|
|
this.finishDialogVisible = true
|
|
},
|
|
deleteProof (row) {
|
|
const current = row || this.currentRow
|
|
if (!current || !current.trackingId) {
|
|
this.$message.warning('请先选择记录')
|
|
return
|
|
}
|
|
this.$confirm('确定删除该打样记录吗?该操作仅删除打样记录,不会删除项目和项目物料。', '提示', {
|
|
confirmButtonText: '确定',
|
|
cancelButtonText: '取消',
|
|
type: 'warning'
|
|
}).then(() => {
|
|
deleteProofTracking({
|
|
trackingId: current.trackingId,
|
|
updateBy: this.$store.state.user.name
|
|
}).then(({ data }) => {
|
|
if (data && data.code === 0) {
|
|
this.$message.success('打样记录删除成功')
|
|
this.processHistoryMap = {}
|
|
this.resetProcessTooltip()
|
|
this.getDataList()
|
|
} else {
|
|
this.$message.error((data && data.msg) || '删除打样记录失败')
|
|
}
|
|
}).catch(() => {
|
|
this.$message.error('删除打样记录异常')
|
|
})
|
|
}).catch(() => {})
|
|
},
|
|
submitFinishProof () {
|
|
if (!this.finishForm.trackingId) {
|
|
this.$message.warning('跟踪记录ID不能为空')
|
|
return
|
|
}
|
|
if (!this.finishForm.actualityDeliveryDate) {
|
|
this.$message.warning('请选择实际完成日期')
|
|
return
|
|
}
|
|
this.finishSaveLoading = true
|
|
finishProofTracking({
|
|
trackingId: this.finishForm.trackingId,
|
|
actualityDeliveryDate: this.finishForm.actualityDeliveryDate,
|
|
updateBy: this.$store.state.user.name
|
|
}).then(({ data }) => {
|
|
this.finishSaveLoading = false
|
|
if (data && data.code === 0) {
|
|
this.$message.success('打样完成成功')
|
|
this.finishDialogVisible = false
|
|
this.getDataList()
|
|
} else {
|
|
this.$message.error(data.msg || '打样完成失败')
|
|
}
|
|
}).catch(() => {
|
|
this.finishSaveLoading = false
|
|
this.$message.error('打样完成异常')
|
|
})
|
|
},
|
|
getProcessCacheKey (row, processCol) {
|
|
if (!row || !processCol || !row.trackingId) {
|
|
return ''
|
|
}
|
|
return `${row.trackingId}_${processCol.code}`
|
|
},
|
|
resetProcessTooltip () {
|
|
this.processTooltipHoverKey = ''
|
|
this.processTooltip.visible = false
|
|
this.processTooltip.content = ''
|
|
},
|
|
hideProcessTooltip () {
|
|
this.processTooltip.visible = false
|
|
this.processTooltip.content = ''
|
|
},
|
|
updateProcessTooltipPosition (event) {
|
|
if (!event) {
|
|
return
|
|
}
|
|
const offsetX = 12
|
|
const offsetY = 16
|
|
let left = event.clientX + offsetX
|
|
let top = event.clientY + offsetY
|
|
const maxWidth = 220
|
|
const maxHeight = 120
|
|
if (left + maxWidth > window.innerWidth - 8) {
|
|
left = window.innerWidth - maxWidth - 8
|
|
}
|
|
if (top + maxHeight > window.innerHeight - 8) {
|
|
top = event.clientY - maxHeight - 8
|
|
}
|
|
this.processTooltip.left = Math.max(8, left)
|
|
this.processTooltip.top = Math.max(8, top)
|
|
},
|
|
handleProcessTooltipMouseMove (event) {
|
|
if (!this.processTooltip.visible) {
|
|
return
|
|
}
|
|
this.updateProcessTooltipPosition(event)
|
|
},
|
|
handleProcessTooltipMouseLeave () {
|
|
this.processTooltipHoverKey = ''
|
|
this.hideProcessTooltip()
|
|
},
|
|
handleProcessTooltipMouseEnter (row, processCol, event) {
|
|
const cacheKey = this.getProcessCacheKey(row, processCol)
|
|
if (!cacheKey) {
|
|
return
|
|
}
|
|
this.processTooltipHoverKey = cacheKey
|
|
this.updateProcessTooltipPosition(event)
|
|
const showTooltip = () => {
|
|
if (this.processTooltipHoverKey !== cacheKey) {
|
|
return
|
|
}
|
|
if (!this.hasProcessHistory(row, processCol)) {
|
|
this.hideProcessTooltip()
|
|
return
|
|
}
|
|
const content = this.getProcessTooltip(row, processCol)
|
|
if (!content) {
|
|
this.hideProcessTooltip()
|
|
return
|
|
}
|
|
this.processTooltip.content = content
|
|
this.processTooltip.visible = true
|
|
}
|
|
if (Object.prototype.hasOwnProperty.call(this.processHistoryMap, cacheKey)) {
|
|
showTooltip()
|
|
return
|
|
}
|
|
this.loadProcessHistory(row, processCol, false).then(() => {
|
|
showTooltip()
|
|
})
|
|
},
|
|
loadProcessHistory (row, processCol, refreshDialog) {
|
|
if (!row || !row.trackingId) {
|
|
return Promise.resolve([])
|
|
}
|
|
const cacheKey = this.getProcessCacheKey(row, processCol)
|
|
if (Object.prototype.hasOwnProperty.call(this.processHistoryMap, cacheKey)) {
|
|
const cachedRows = this.processHistoryMap[cacheKey] || []
|
|
if (refreshDialog) {
|
|
this.processHistoryRows = cachedRows
|
|
}
|
|
return Promise.resolve(cachedRows)
|
|
}
|
|
return queryProofTrackingProcessHistory({
|
|
trackingId: row.trackingId,
|
|
processCode: processCol.code
|
|
}).then(({ data }) => {
|
|
let rows = []
|
|
if (data && data.code === 0) {
|
|
rows = data.rows || []
|
|
// Vue2 对对象新增 key 需要使用 $set 才能触发视图更新(tooltip 才会刷新历史列表)
|
|
this.$set(this.processHistoryMap, cacheKey, rows)
|
|
if (refreshDialog) {
|
|
this.processHistoryRows = rows
|
|
}
|
|
}
|
|
return rows
|
|
}).catch(() => {
|
|
if (refreshDialog) {
|
|
this.processHistoryRows = []
|
|
}
|
|
this.$set(this.processHistoryMap, cacheKey, [])
|
|
return []
|
|
})
|
|
},
|
|
hasProcessHistory (row, processCol) {
|
|
if (!row || !processCol || !row.trackingId) {
|
|
return false
|
|
}
|
|
const cacheKey = `${row.trackingId}_${processCol.code}`
|
|
const rows = this.processHistoryMap[cacheKey] || []
|
|
return rows.length > 0
|
|
},
|
|
getProcessTooltip (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return ''
|
|
}
|
|
const cacheKey = row.trackingId ? `${row.trackingId}_${processCol.code}` : ''
|
|
const rows = this.processHistoryMap[cacheKey] || []
|
|
|
|
const dateList = []
|
|
const pushDate = (dateVal) => {
|
|
const formatted = this.formatDate(dateVal)
|
|
if (!formatted) {
|
|
return
|
|
}
|
|
if (dateList.indexOf(formatted) === -1) {
|
|
dateList.push(formatted)
|
|
}
|
|
}
|
|
|
|
// 先放当前实际完成日期,再放历史变更日期(实际)
|
|
pushDate(row[processCol.actualField])
|
|
rows.forEach(item => {
|
|
pushDate(item.newActualDate)
|
|
pushDate(item.oldActualDate)
|
|
})
|
|
|
|
if (dateList.length <= 1) {
|
|
return ''
|
|
}
|
|
// tooltip 只展示历史日期,不展示最近一次日期(当前值)以避免重复。
|
|
const latestDate = dateList.reduce((max, currentDate) => {
|
|
if (!max || currentDate > max) {
|
|
return currentDate
|
|
}
|
|
return max
|
|
}, '')
|
|
const historyDateList = dateList.filter(item => item !== latestDate)
|
|
if (historyDateList.length === 0) {
|
|
return ''
|
|
}
|
|
return historyDateList.join('\n')
|
|
},
|
|
isProcessStatusComplete (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return false
|
|
}
|
|
const statusVal = row[processCol.statusField]
|
|
const status = statusVal ? String(statusVal).trim() : ''
|
|
if (!status || status === '未完成' || status === '进行中') {
|
|
return false
|
|
}
|
|
if (status === '已完成' || status === '打样完成') {
|
|
return true
|
|
}
|
|
return status.indexOf('完成') > -1 && status.indexOf('未') === -1
|
|
},
|
|
isProcessComplete (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return false
|
|
}
|
|
const statusVal = row[processCol.statusField]
|
|
const status = statusVal ? String(statusVal).trim() : ''
|
|
|
|
if (!status) {
|
|
// 兼容历史数据:若没有状态但有实际完成日期,也按完成处理
|
|
return !!this.formatDate(row[processCol.actualField])
|
|
}
|
|
if (status === '未完成' || status === '进行中') {
|
|
return false
|
|
}
|
|
if (status === '已完成' || status === '打样完成') {
|
|
return true
|
|
}
|
|
return status.indexOf('完成') > -1 && status.indexOf('未') === -1
|
|
},
|
|
getTrackingProcessColumnsByRow (row) {
|
|
if (!row) {
|
|
return []
|
|
}
|
|
const processColumnMap = {}
|
|
const appendProcessColumn = (processCol) => {
|
|
if (!processCol || !processCol.code || processColumnMap[processCol.code]) {
|
|
return
|
|
}
|
|
processColumnMap[processCol.code] = processCol
|
|
}
|
|
this.processColumns.forEach(item => {
|
|
appendProcessColumn(item)
|
|
})
|
|
// 兜底补齐默认工序元信息,避免配置变化导致状态字段找不到。
|
|
DEFAULT_PROCESS_COLUMNS.forEach(item => {
|
|
appendProcessColumn(item)
|
|
})
|
|
|
|
let orderedCodes = []
|
|
if (this.getSelectedSearchBuNo()) {
|
|
orderedCodes = this.visibleProcessColumns.map(item => item.code)
|
|
} else {
|
|
const configEntry = this.getBuProcessConfigEntry(row.buNo, row.site)
|
|
const config = configEntry ? configEntry.config : null
|
|
orderedCodes = config && Array.isArray(config.processOrderCodes) && config.processOrderCodes.length > 0
|
|
? this.getValidProcessOrderCodes(config.processOrderCodes)
|
|
: this.getAllProcessCodes()
|
|
const visibleCodes = this.getProcessVisibleCodesByBuNo(row.buNo, row.site)
|
|
if (visibleCodes) {
|
|
const visibleCodeMap = {}
|
|
visibleCodes.forEach(code => {
|
|
visibleCodeMap[code] = true
|
|
})
|
|
orderedCodes = orderedCodes.filter(code => visibleCodeMap[code])
|
|
}
|
|
}
|
|
|
|
const result = []
|
|
const usedCodeMap = {}
|
|
orderedCodes.forEach(code => {
|
|
const codeVal = code == null ? '' : String(code).trim()
|
|
if (!codeVal || usedCodeMap[codeVal]) {
|
|
return
|
|
}
|
|
const processCol = processColumnMap[codeVal]
|
|
if (!processCol) {
|
|
return
|
|
}
|
|
usedCodeMap[codeVal] = true
|
|
result.push(processCol)
|
|
})
|
|
return result
|
|
},
|
|
getTrackingStatusProcessLabel (row, processCol) {
|
|
if (!row || !processCol) {
|
|
return ''
|
|
}
|
|
const processCategoryName = processCol.processCategoryName == null
|
|
? ''
|
|
: String(processCol.processCategoryName).trim()
|
|
if (processCategoryName) {
|
|
return processCategoryName
|
|
}
|
|
const processCategoryCode = this.getBuProcessCategoryCodeByProcessCode(row.buNo, processCol.code)
|
|
const buCategoryName = this.getBuProcessCategoryNameByCode(row.buNo, processCategoryCode)
|
|
if (buCategoryName) {
|
|
return buCategoryName
|
|
}
|
|
return processCol.label || processCol.code
|
|
},
|
|
getPendingProcessColumnForTrackingStatus (row) {
|
|
const processColumns = this.getTrackingProcessColumnsByRow(row)
|
|
let firstPendingWithDate = null
|
|
let firstPendingDate = ''
|
|
let firstPendingWithoutDate = null
|
|
for (let i = 0; i < processColumns.length; i += 1) {
|
|
const processCol = processColumns[i]
|
|
if (this.isProcessComplete(row, processCol)) {
|
|
continue
|
|
}
|
|
const actualDate = this.formatDate(row[processCol.actualField])
|
|
if (!actualDate) {
|
|
// 无日期的未完成工序仅作为兜底:优先展示“有日期的未完成工序”。
|
|
if (!firstPendingWithoutDate) {
|
|
firstPendingWithoutDate = processCol
|
|
}
|
|
continue
|
|
}
|
|
// 同日期按工序顺序兜底:仅在日期更小时替换,日期相同保留先命中的工序。
|
|
if (!firstPendingWithDate || actualDate < firstPendingDate) {
|
|
firstPendingWithDate = processCol
|
|
firstPendingDate = actualDate
|
|
}
|
|
}
|
|
return firstPendingWithDate || firstPendingWithoutDate
|
|
},
|
|
getFirstPendingProcessLabel (row) {
|
|
const processCol = this.getPendingProcessColumnForTrackingStatus(row)
|
|
if (!processCol) {
|
|
return '已全部完成'
|
|
}
|
|
return this.getTrackingStatusProcessLabel(row, processCol)
|
|
},
|
|
getTrackingStatusType (row) {
|
|
if (!row) {
|
|
return ''
|
|
}
|
|
const requiredDeliveryDate = this.formatDate(row.requiredDeliveryDate)
|
|
if (!requiredDeliveryDate) {
|
|
return ''
|
|
}
|
|
const today = this.formatDate(new Date())
|
|
// 按业务口径:今日 <= 预计完成日期 判定为 On schedule,其余判定为 Delay。
|
|
if (today <= requiredDeliveryDate) {
|
|
return 'onSchedule'
|
|
}
|
|
return 'delay'
|
|
},
|
|
getTrackingStatusText (row) {
|
|
const statusType = this.getTrackingStatusType(row)
|
|
if (!statusType) {
|
|
return '-'
|
|
}
|
|
const statusPrefix = statusType === 'onSchedule' ? 'On schedule' : 'Delay'
|
|
return `${statusPrefix}-${this.getFirstPendingProcessLabel(row)}`
|
|
},
|
|
getTrackingStatusClass (row) {
|
|
const statusType = this.getTrackingStatusType(row)
|
|
if (statusType === 'onSchedule') {
|
|
return 'tracking-status tracking-status--on-schedule'
|
|
}
|
|
if (statusType === 'delay') {
|
|
return 'tracking-status tracking-status--delay'
|
|
}
|
|
return 'tracking-status tracking-status--unknown'
|
|
},
|
|
formatDate (val) {
|
|
if (!val) {
|
|
return ''
|
|
}
|
|
if (typeof val === 'string') {
|
|
return val.length > 10 ? val.substring(0, 10) : val
|
|
}
|
|
const date = new Date(val)
|
|
if (isNaN(date.getTime())) {
|
|
return ''
|
|
}
|
|
const y = date.getFullYear()
|
|
const m = `${date.getMonth() + 1}`.padStart(2, '0')
|
|
const d = `${date.getDate()}`.padStart(2, '0')
|
|
return `${y}-${m}-${d}`
|
|
},
|
|
formatDateTime (val) {
|
|
if (!val) {
|
|
return ''
|
|
}
|
|
if (typeof val === 'string') {
|
|
return val.length > 19 ? val.substring(0, 19) : val
|
|
}
|
|
const date = new Date(val)
|
|
if (isNaN(date.getTime())) {
|
|
return ''
|
|
}
|
|
const y = date.getFullYear()
|
|
const m = `${date.getMonth() + 1}`.padStart(2, '0')
|
|
const d = `${date.getDate()}`.padStart(2, '0')
|
|
const hh = `${date.getHours()}`.padStart(2, '0')
|
|
const mm = `${date.getMinutes()}`.padStart(2, '0')
|
|
const ss = `${date.getSeconds()}`.padStart(2, '0')
|
|
return `${y}-${m}-${d} ${hh}:${mm}:${ss}`
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style >
|
|
.data-table .cell {
|
|
line-height: 26px;
|
|
font-size: 12px;
|
|
height: 28px;
|
|
}
|
|
|
|
.search-form {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.button-group-col {
|
|
width: 100%;
|
|
margin-top: 16px;
|
|
}
|
|
|
|
.button-row {
|
|
width: 100%;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-list {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(220px, 1fr));
|
|
column-gap: 12px;
|
|
row-gap: 8px;
|
|
align-items: center;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-list-column .cell {
|
|
white-space: normal !important;
|
|
height: auto !important;
|
|
line-height: 20px;
|
|
padding-top: 6px;
|
|
padding-bottom: 6px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-list-column {
|
|
vertical-align: top;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
min-height: 28px;
|
|
padding: 2px 6px;
|
|
border: 1px solid transparent;
|
|
border-radius: 4px;
|
|
cursor: move;
|
|
transition: border-color .2s ease, background-color .2s ease;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item:hover {
|
|
border-color: #7cc4ff;
|
|
background: #f5fbff;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item.is-dragging {
|
|
opacity: .55;
|
|
border-color: #409eff;
|
|
background: #ecf5ff;
|
|
}
|
|
|
|
.bu-process-config-dialog .process-order-index {
|
|
width: 22px;
|
|
color: #606266;
|
|
font-size: 12px;
|
|
text-align: right;
|
|
margin-right: 4px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.bu-process-config-dialog .process-order-handle {
|
|
margin-right: 6px;
|
|
color: #909399;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item .el-checkbox,
|
|
.bu-process-config-dialog .bu-process-order-item .el-checkbox + .el-checkbox {
|
|
width: 100%;
|
|
margin: 0 !important;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item .el-checkbox__label {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-order-item .el-checkbox {
|
|
flex: 1;
|
|
min-width: 0;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-category-select {
|
|
width: 100px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-config-tip {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-bottom: 8px;
|
|
padding: 6px 10px;
|
|
border-radius: 4px;
|
|
background: #f4f8ff;
|
|
color: #5f6b7a;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.bu-process-config-dialog .bu-process-config-tip i {
|
|
color: #409eff;
|
|
}
|
|
|
|
.default-process-config-dialog .default-process-config-actions {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.default-process-config-dialog .bu-process-config-tip {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-bottom: 8px;
|
|
padding: 6px 10px;
|
|
border-radius: 4px;
|
|
background: #f4f8ff;
|
|
color: #5f6b7a;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.default-process-config-dialog .bu-process-config-tip i {
|
|
color: #409eff;
|
|
}
|
|
|
|
.default-process-config-dialog .default-process-config-empty {
|
|
margin: 12px 0;
|
|
padding: 12px;
|
|
border: 1px dashed #dcdfe6;
|
|
border-radius: 4px;
|
|
color: #909399;
|
|
text-align: center;
|
|
}
|
|
|
|
.default-process-config-dialog .default-process-drag-handle {
|
|
color: #909399;
|
|
cursor: move;
|
|
font-size: 15px;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
.default-process-config-dialog .default-process-order-index {
|
|
color: #606266;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.default-process-config-dialog .el-table__body-wrapper tbody tr {
|
|
cursor: move;
|
|
}
|
|
|
|
.default-process-config-dialog .el-table__body-wrapper tbody tr .el-input__inner {
|
|
cursor: text;
|
|
}
|
|
|
|
.default-process-config-dialog .el-table__body-wrapper tbody tr .el-checkbox__inner,
|
|
.default-process-config-dialog .el-table__body-wrapper tbody tr .el-select .el-input__inner {
|
|
cursor: pointer;
|
|
}
|
|
|
|
.process-category-config-dialog .default-process-config-actions {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.process-category-config-dialog .el-table__body-wrapper tbody tr .el-input__inner {
|
|
cursor: text;
|
|
}
|
|
|
|
.default-process-sortable-ghost > td {
|
|
background: #f0f9ff !important;
|
|
}
|
|
|
|
.default-process-sortable-chosen > td {
|
|
background: #ecf5ff !important;
|
|
}
|
|
|
|
.button-col {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.button-col .el-button {
|
|
width: 100%;
|
|
}
|
|
|
|
.one-key-grid-form {
|
|
padding: 0 8px;
|
|
}
|
|
|
|
.one-key-grid-form .el-form-item {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.one-key-suggest-main {
|
|
color: #303133;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.one-key-suggest-sub {
|
|
color: #909399;
|
|
font-size: 12px;
|
|
line-height: 16px;
|
|
}
|
|
|
|
.proof-grid-form {
|
|
padding: 0 8px;
|
|
}
|
|
|
|
.proof-grid-form .el-form-item {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.apply-qty-reference {
|
|
margin-top: 6px;
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
background: #ecf5ff;
|
|
color: #3d77b1;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.big-label a {
|
|
color: #35b4b4;
|
|
}
|
|
|
|
.sync-text--ok {
|
|
color: #67c23a;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.sync-text--pending {
|
|
color: #606266;
|
|
}
|
|
|
|
.tracking-status {
|
|
width: 100%;
|
|
min-height: 26px;
|
|
padding: 4px 6px;
|
|
box-sizing: border-box;
|
|
border: 1px solid #dcdfe6;
|
|
border-radius: 4px;
|
|
background: #fff;
|
|
color: #303133;
|
|
font-weight: 500;
|
|
line-height: 18px;
|
|
text-align: left;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.tracking-status--on-schedule {
|
|
background: #e8f5e9;
|
|
}
|
|
|
|
.tracking-status--delay {
|
|
background: #fde2e2;
|
|
}
|
|
|
|
.tracking-status--unknown {
|
|
background: #f4f4f5;
|
|
}
|
|
|
|
.process-cell {
|
|
background: #fff;
|
|
min-height: 44px;
|
|
padding: 4px 2px;
|
|
}
|
|
|
|
.process-cell.is-complete {
|
|
background: #ececec;
|
|
}
|
|
|
|
.process-cell.is-complete .el-input__inner {
|
|
background: #ececec;
|
|
border-color: #d5d7de;
|
|
color: #8c8c8c;
|
|
}
|
|
|
|
.process-cell.is-complete .el-input.is-disabled .el-input__inner {
|
|
background: #ececec;
|
|
color: #8c8c8c;
|
|
}
|
|
|
|
.process-cell.is-complete .link-date {
|
|
color: #8c8c8c;
|
|
}
|
|
|
|
.process-cell.is-complete .link-date:hover,
|
|
.process-cell.is-complete .link-date:focus {
|
|
color: #8c8c8c;
|
|
}
|
|
|
|
.process-cell--masked {
|
|
background: transparent !important;
|
|
}
|
|
|
|
.process-content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 4px;
|
|
text-align: center;
|
|
line-height: 18px;
|
|
}
|
|
|
|
.process-complete-btn {
|
|
padding: 4px 8px;
|
|
min-width: 12px;
|
|
}
|
|
|
|
.comments-cell {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
min-height: 26px;
|
|
}
|
|
|
|
.comments-edit-input {
|
|
flex: 1;
|
|
}
|
|
|
|
.comments-save-btn {
|
|
padding: 4px 7px;
|
|
min-width: 28px;
|
|
}
|
|
|
|
.comments-text {
|
|
width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.process-date-tooltip {
|
|
white-space: pre-line;
|
|
}
|
|
|
|
.process-history-tooltip {
|
|
position: fixed;
|
|
z-index: 3000;
|
|
max-width: 220px;
|
|
padding: 10px 12px;
|
|
border-radius: 4px;
|
|
background: rgba(48, 49, 51, 0.95);
|
|
color: #fff;
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
white-space: pre-line;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.link-date {
|
|
color: #0c4dbb;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.link-date:hover,
|
|
.link-date:focus {
|
|
text-decoration: none;
|
|
}
|
|
|
|
.actual-date {
|
|
margin-top: 2px;
|
|
color: #666;
|
|
}
|
|
|
|
.finish-dialog-tip {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
margin-bottom: 10px;
|
|
padding: 8px 10px;
|
|
border-radius: 4px;
|
|
background: #f4f8ff;
|
|
color: #3a4b5f;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.finish-dialog-tip i {
|
|
color: #409eff;
|
|
}
|
|
|
|
.finish-tip-highlight {
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: #1f2d3d;
|
|
}
|
|
|
|
.finish-proof-dialog .el-dialog__body {
|
|
padding-bottom: 8px;
|
|
}
|
|
|
|
.finish-form .el-form-item {
|
|
margin-bottom: 14px;
|
|
}
|
|
|
|
.action-link {
|
|
color: #0c4dbb;
|
|
margin: 0 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.edit-link {
|
|
color: #e6a23c;
|
|
}
|
|
|
|
.delete-link {
|
|
color: #f56c6c;
|
|
}
|
|
|
|
.end-link {
|
|
color: #a303ff;
|
|
}
|
|
|
|
.zxClass .cell {
|
|
line-height: 24px;
|
|
font-size: 12px;
|
|
height: 24px;
|
|
}
|
|
</style>
|
|
|