Browse Source

汇率;打样状态可以改为进行中

master
han\hanst 5 days ago
parent
commit
c2f58c61cf
  1. 3
      src/api/sampleTracking/sampleTracking.js
  2. 708
      src/views/modules/sampleTracking/projectProofTracking.vue

3
src/api/sampleTracking/sampleTracking.js

@ -23,6 +23,9 @@ export const oneKeyUpdateProofTracking = data => createAPI('/sampleTracking/proo
export const createProofTrackingRecord = data => createAPI('/sampleTracking/proofTracking/createProofRecord', 'POST', data) export const createProofTrackingRecord = data => createAPI('/sampleTracking/proofTracking/createProofRecord', 'POST', data)
export const updateProofTrackingProcess = data => createAPI('/sampleTracking/proofTracking/updateProcess', 'POST', data) export const updateProofTrackingProcess = data => createAPI('/sampleTracking/proofTracking/updateProcess', 'POST', data)
export const updateProofTrackingComments = data => createAPI('/sampleTracking/proofTracking/updateComments', 'POST', data) export const updateProofTrackingComments = data => createAPI('/sampleTracking/proofTracking/updateComments', 'POST', data)
export const updateProofTrackingCost = data => createAPI('/sampleTracking/proofTracking/updateCost', 'POST', data)
export const saveProofTrackingExchangeRate = data => createAPI('/sampleTracking/proofTracking/saveExchangeRate', 'POST', data)
export const refreshProofTrackingCostByRate = data => createAPI('/sampleTracking/proofTracking/refreshCostByRate', 'POST', data)
export const finishProofTracking = data => createAPI('/sampleTracking/proofTracking/finishProofing', 'POST', data) export const finishProofTracking = data => createAPI('/sampleTracking/proofTracking/finishProofing', 'POST', data)
export const deleteProofTracking = data => createAPI('/sampleTracking/proofTracking/delete', 'POST', data) export const deleteProofTracking = data => createAPI('/sampleTracking/proofTracking/delete', 'POST', data)
export const deleteProofTrackingBatch = data => createAPI('/sampleTracking/proofTracking/deleteBatch', 'POST', data) export const deleteProofTrackingBatch = data => createAPI('/sampleTracking/proofTracking/deleteBatch', 'POST', data)

708
src/views/modules/sampleTracking/projectProofTracking.vue

@ -58,6 +58,7 @@
>事业部进度设置</el-button> >事业部进度设置</el-button>
<el-button type="info" v-if="isAuth('833001005:set2')" plain @click="openDefaultProcessConfigDialog">默认进度设置</el-button> <el-button type="info" v-if="isAuth('833001005:set2')" plain @click="openDefaultProcessConfigDialog">默认进度设置</el-button>
<el-button type="danger" plain :loading="deleteProofLoading" @click="deleteSelectedProofs">删除</el-button> <el-button type="danger" plain :loading="deleteProofLoading" @click="deleteSelectedProofs">删除</el-button>
<el-button type="warning" plain :loading="costRateRecalcLoading" @click="recalculateCostByRate">按汇率重算</el-button>
<el-tooltip <el-tooltip
style="margin-top: 3px; margin-left: 10px" style="margin-top: 3px; margin-left: 10px"
placement="top"> placement="top">
@ -67,6 +68,19 @@
</template> </template>
<i class="el-icon-question default-process-tip-icon"></i> <i class="el-icon-question default-process-tip-icon"></i>
</el-tooltip> </el-tooltip>
<span class="cost-rate-tip">
汇率
<el-input
v-model="usdCnyExchangeRateDraft"
class="cost-rate-input"
size="mini"
clearable
placeholder="未设置"
:disabled="costRateSaving"
@blur="saveExchangeRate"
@keyup.enter.native="$event.target.blur()"
/>
</span>
</div> </div>
</div> </div>
<el-form v-show="searchExpanded" :inline="true" :model="searchData" label-position="top" class="search-form"> <el-form v-show="searchExpanded" :inline="true" :model="searchData" label-position="top" class="search-form">
@ -298,6 +312,49 @@
v-else-if="item.columnProp === 'deliveryVariance'" v-else-if="item.columnProp === 'deliveryVariance'"
:class="{ 'delivery-variance-alert': isDeliveryVarianceAlert(scope.row) }" :class="{ 'delivery-variance-alert': isDeliveryVarianceAlert(scope.row) }"
>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span> >{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span>
<div
v-else-if="isCostManualColumn(item.columnProp)"
class="cost-cell"
@click.stop="startCostEdit(scope.row, item.columnProp)"
>
<el-select
v-if="isCostEditing(scope.row, item.columnProp) && item.columnProp === 'customizedForMasAls'"
ref="costEditor"
v-model="costEditDraft"
class="cost-editor"
size="mini"
filterable
allow-create
default-first-option
clearable
placeholder="请选择或输入"
@visible-change="handleCostSelectVisible($event, scope.row, item.columnProp)"
@keyup.esc.native="cancelCostEdit(scope.row)"
>
<el-option
v-for="option in customizedForMasAlsOptions"
:key="option"
:label="option"
:value="option"
/>
</el-select>
<el-input
v-else-if="isCostEditing(scope.row, item.columnProp)"
ref="costEditor"
v-model="costEditDraft"
class="cost-editor"
size="mini"
@keydown.enter.native.prevent="saveCostEdit(scope.row, item.columnProp)"
@keyup.esc.native="cancelCostEdit(scope.row)"
@blur="saveCostEdit(scope.row, item.columnProp)"
/>
<span v-else class="cost-display cost-display--editable">{{ getCostManualDisplay(scope.row, item.columnProp) }}</span>
</div>
<span
v-else-if="isCostCalculatedColumn(item.columnProp)"
class="cost-calculated-text"
:title="getCostCalculatedTitle(scope.row, item.columnProp)"
>{{ formatCostAmount(getCostCalculatedValue(scope.row, item.columnProp)) }}</span>
<span v-else>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span> <span v-else>{{ getTrackingColumnDisplayValue(scope.row, item.columnProp) }}</span>
</template> </template>
</el-table-column> </el-table-column>
@ -917,7 +974,7 @@
</el-col> </el-col>
<el-col :span="6" v-show="isOneKeyCreateFieldVisible('proofingStatus')"> <el-col :span="6" v-show="isOneKeyCreateFieldVisible('proofingStatus')">
<el-form-item label="打样状态"> <el-form-item label="打样状态">
<!-- 新建固定草稿;修改只允许改成终止/取消/报废,选项按打开弹窗时的原状态生成,避免选中后丢掉“保持原状态”。 -->
<!-- 新建固定草稿。修改默认可改成终止/取消/报废;只有打开时原状态已是这三种,才额外给出“进行中”。选项锚定原状态,避免选中后无法改回。 -->
<el-select <el-select
v-model="oneKeyForm.proofingStatus" v-model="oneKeyForm.proofingStatus"
:disabled="oneKeyDialogMode === 'create'" :disabled="oneKeyDialogMode === 'create'"
@ -1295,6 +1352,9 @@ import {
batchSyncProofTracking, batchSyncProofTracking,
batchSyncProjectPartTracking, batchSyncProjectPartTracking,
updateProofTrackingComments, updateProofTrackingComments,
updateProofTrackingCost,
saveProofTrackingExchangeRate,
refreshProofTrackingCostByRate,
updateProofTrackingProcess updateProofTrackingProcess
} from '@/api/sampleTracking/sampleTracking' } from '@/api/sampleTracking/sampleTracking'
import { searchExpApplyList } from '@/api/erf/erf' import { searchExpApplyList } from '@/api/erf/erf'
@ -1398,6 +1458,15 @@ const ONE_KEY_ROLE_FIELDS = [
'docEngineer2' 'docEngineer2'
] ]
const COST_MANUAL_COLUMNS = ['customizedForMasAls', 'tpKctUsd', 'toolingCostCn', 'sampleCostFromFnCn']
const COST_CALCULATED_COLUMNS = ['sampleCostUsd', 'toolingCostUsd', 'sampleAndToolingCostUsd', 'sampleCostFromFnUsd']
const CUSTOMIZED_FOR_MAS_ALS_OPTIONS = [
'PM-standard',
'PM-Customized for ALS',
'PM-Customized for MAS',
'PO'
]
const DEFAULT_TRACKING_COLUMNS = [ const DEFAULT_TRACKING_COLUMNS = [
{ {
serialNumber: 'proofTrackingTable1ProjectNo', serialNumber: 'proofTrackingTable1ProjectNo',
@ -1525,6 +1594,21 @@ const DEFAULT_TRACKING_COLUMNS = [
columnWidth: 120, columnWidth: 120,
showOverflowTooltip: true showOverflowTooltip: true
}, },
// 紧跟 PM/Sales:下拉可选,也允许手工录入文本。
{
serialNumber: 'proofTrackingTable1CustomizedForMasAls',
columnProp: 'customizedForMasAls',
headerAlign: 'center',
align: 'left',
columnLabel: 'Customized for MAS/ALS',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 180,
showOverflowTooltip: true
},
{ {
serialNumber: 'proofTrackingTable1ProofingNo', serialNumber: 'proofTrackingTable1ProofingNo',
columnProp: 'proofingNo', columnProp: 'proofingNo',
@ -1568,6 +1652,105 @@ const DEFAULT_TRACKING_COLUMNS = [
columnWidth: 90, columnWidth: 90,
showOverflowTooltip: false showOverflowTooltip: false
}, },
// 成本列紧跟 Sample Qty:单价、模具费、样品费都按数量折算,和数量放在一起看。
{
serialNumber: 'proofTrackingTable1TpKctUsd',
columnProp: 'tpKctUsd',
headerAlign: 'center',
align: 'center',
columnLabel: 'TP(KCT/USD)',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 120,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1SampleCostUsd',
columnProp: 'sampleCostUsd',
headerAlign: 'center',
align: 'center',
columnLabel: 'Sample Cost/USD',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 140,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1ToolingCostCn',
columnProp: 'toolingCostCn',
headerAlign: 'center',
align: 'center',
columnLabel: 'Tooling cost/CN',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 140,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1ToolingCostUsd',
columnProp: 'toolingCostUsd',
headerAlign: 'center',
align: 'center',
columnLabel: 'Tooling Cost/USD',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 150,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1SampleAndToolingCostUsd',
columnProp: 'sampleAndToolingCostUsd',
headerAlign: 'center',
align: 'center',
columnLabel: 'Sample and Tooling Cost/USD',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 210,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1SampleCostFromFnCn',
columnProp: 'sampleCostFromFnCn',
headerAlign: 'center',
align: 'center',
columnLabel: 'Sample Cost from FN/CN',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 180,
showOverflowTooltip: false
},
{
serialNumber: 'proofTrackingTable1SampleCostFromFnUsd',
columnProp: 'sampleCostFromFnUsd',
headerAlign: 'center',
align: 'center',
columnLabel: 'Sample Cost from FN/USD',
columnHidden: false,
columnImage: false,
columnSortable: false,
status: true,
fixed: '',
columnWidth: 190,
showOverflowTooltip: false
},
{ {
serialNumber: 'proofTrackingTable1PmInqueryTime', serialNumber: 'proofTrackingTable1PmInqueryTime',
columnProp: 'pmInqueryTime', columnProp: 'pmInqueryTime',
@ -1907,6 +2090,15 @@ export default {
commentsEditingTrackingId: null, commentsEditingTrackingId: null,
commentsDraft: '', commentsDraft: '',
commentsSaveLoading: false, commentsSaveLoading: false,
costEditingKey: '',
costEditDraft: '',
costEditOriginal: '',
costSelectReady: false,
usdCnyExchangeRate: null,
usdCnyExchangeRateDraft: '',
costRateSaving: false,
costRateRecalcLoading: false,
customizedForMasAlsOptions: CUSTOMIZED_FOR_MAS_ALS_OPTIONS,
// 工序日期按需编辑:空字符串表示未打开日期组件。 // 工序日期按需编辑:空字符串表示未打开日期组件。
processDateEditingKey: '', processDateEditingKey: '',
@ -1971,6 +2163,7 @@ export default {
this.destroyDefaultProcessConfigSortable() this.destroyDefaultProcessConfigSortable()
this.clearProcessDateEditClickTimer() this.clearProcessDateEditClickTimer()
this.exitProcessDateEdit() this.exitProcessDateEdit()
this.exitCostEdit()
}, },
beforeDestroy () { beforeDestroy () {
if (this.$store.state.common.contentIsNeedRefresh) { if (this.$store.state.common.contentIsNeedRefresh) {
@ -1982,6 +2175,7 @@ export default {
} }
this.clearProcessDateEditClickTimer() this.clearProcessDateEditClickTimer()
this.exitProcessDateEdit() this.exitProcessDateEdit()
this.exitCostEdit()
this.unbindTrackingTableResize() this.unbindTrackingTableResize()
}, },
watch: { watch: {
@ -2056,12 +2250,16 @@ export default {
if (this.oneKeyDialogMode === 'create') { if (this.oneKeyDialogMode === 'create') {
return ['草稿', '进行中', '打样完成'] return ['草稿', '进行中', '打样完成']
} }
// 选项锚定打开弹窗时的原状态,避免选中终止后“进行中”从下拉里消失,用户无法在保存前改回。
// 选项锚定打开弹窗时的原状态,避免选中后丢掉当前值、保存前无法改回。
// “进行中”只在原状态为终止/取消/报废时作为可改目标加入;草稿等状态不能改成进行中。
// 原状态本身已是进行中时仍保留该值,否则下拉无法显示当前状态。
const currentStatus = this.oneKeyOriginalProofingStatus const currentStatus = this.oneKeyOriginalProofingStatus
? String(this.oneKeyOriginalProofingStatus).trim() ? String(this.oneKeyOriginalProofingStatus).trim()
: '' : ''
const options = [] const options = []
if (currentStatus && this.proofingStatusChangeOptions.indexOf(currentStatus) === -1) {
if (this.isClosedProofingStatusValue(currentStatus)) {
options.push('进行中')
} else if (currentStatus) {
options.push(currentStatus) options.push(currentStatus)
} }
this.proofingStatusChangeOptions.forEach(item => { this.proofingStatusChangeOptions.forEach(item => {
@ -4387,10 +4585,13 @@ export default {
// 列表会整表换行数据,未提交的工序日期编辑态不能带到新行上。 // 列表会整表换行数据,未提交的工序日期编辑态不能带到新行上。
this.clearProcessDateEditClickTimer() this.clearProcessDateEditClickTimer()
this.exitProcessDateEdit() this.exitProcessDateEdit()
this.exitCostEdit()
searchProofTracking(inData).then(({ data }) => { searchProofTracking(inData).then(({ data }) => {
this.dataListLoading = false this.dataListLoading = false
this.resetProcessTooltip() this.resetProcessTooltip()
if (data && data.code === 0) { if (data && data.code === 0) {
this.usdCnyExchangeRate = data.usdCnyExchangeRate == null ? null : data.usdCnyExchangeRate
this.usdCnyExchangeRateDraft = this.usdCnyExchangeRate == null ? '' : this.formatCostDisplay(this.usdCnyExchangeRate)
this.dataList = (data.page && data.page.list) || [] this.dataList = (data.page && data.page.list) || []
this.syncDataListProcessValues() this.syncDataListProcessValues()
this.resetDataListPageOrderIndexes() this.resetDataListPageOrderIndexes()
@ -4766,6 +4967,438 @@ export default {
this.$message.error('Comments保存异常') this.$message.error('Comments保存异常')
}) })
}, },
isCostManualColumn (columnProp) {
return COST_MANUAL_COLUMNS.indexOf(columnProp) > -1
},
isCostCalculatedColumn (columnProp) {
return COST_CALCULATED_COLUMNS.indexOf(columnProp) > -1
},
isCostEditing (row, columnProp) {
return !!row && this.costEditingKey === `${row.trackingId}:${columnProp}`
},
isCostEditingRow (row) {
if (!row || !this.costEditingKey) {
return false
}
return this.costEditingKey.indexOf(`${row.trackingId}:`) === 0
},
formatCostDisplay (value) {
if (value == null || String(value).trim() === '') {
return ''
}
const number = Number(value)
if (!Number.isFinite(number)) {
return String(value)
}
return number.toFixed(6).replace(/\.?0+$/, '')
},
// 费用金额固定两位。汇率仍走 formatCostDisplay,避免把 6.685421 收成 6.69。
formatCostAmount (value) {
if (value == null || String(value).trim() === '') {
return ''
}
const number = Number(value)
if (!Number.isFinite(number)) {
return String(value)
}
return this.roundCost(number).toFixed(2)
},
roundCost (value) {
const number = Number(value)
if (!Number.isFinite(number)) {
return null
}
return Math.round((number + Number.EPSILON) * 100) / 100
},
toCostNumber (value) {
if (value == null || String(value).trim() === '') {
return null
}
const number = Number(value)
return Number.isFinite(number) ? number : null
},
getRowSampleQty (row) {
if (!row) {
return null
}
const qty = row.sampleQty != null && row.sampleQty !== '' ? row.sampleQty : row.proofingNumber
return this.toCostNumber(qty)
},
getCostRateNumber () {
const rate = this.toCostNumber(this.usdCnyExchangeRate)
return rate != null && rate > 0 ? rate : null
},
getCostManualDisplay (row, columnProp) {
if (!row) {
return ''
}
if (columnProp === 'customizedForMasAls') {
return this.isBlankValue(row.customizedForMasAls) ? '' : String(row.customizedForMasAls)
}
return this.formatCostAmount(row[columnProp])
},
normalizeCostDraft (columnProp, rawValue) {
if (columnProp === 'customizedForMasAls') {
const text = rawValue == null ? '' : String(rawValue).trim()
if (!text) {
return { value: null, error: '' }
}
if (text.length > 100) {
return { value: null, error: 'Customized for MAS/ALS不能超过100个字符' }
}
return { value: text, error: '' }
}
const text = rawValue == null ? '' : String(rawValue).trim()
if (!text) {
return { value: null, error: '' }
}
if (!/^\d+(\.\d+)?$/.test(text)) {
return { value: null, error: '请输入不小于0的数字' }
}
const number = Number(text)
if (!Number.isFinite(number) || number >= 1000000000000) {
return { value: null, error: '数值超出可保存范围' }
}
return { value: this.roundCost(number), error: '' }
},
isSameCostDraft (columnProp, normalizedValue, originalDisplay) {
if (columnProp === 'customizedForMasAls') {
const left = normalizedValue == null ? '' : String(normalizedValue).trim()
const right = originalDisplay == null ? '' : String(originalDisplay).trim()
return left === right
}
return this.formatCostAmount(normalizedValue) === this.formatCostAmount(originalDisplay)
},
sumPreviewCost (left, right) {
if (left == null && right == null) {
return null
}
return this.roundCost((left || 0) + (right || 0))
},
// 列表默认显示已保存结果。只有正在改的那个数字,才按当天汇率预览受影响的计算列。
getCostCalculatedValue (row, columnProp) {
if (!row) {
return null
}
if (!this.isCostEditingRow(row)) {
return row[columnProp]
}
const editingProp = this.costEditingKey.slice(this.costEditingKey.indexOf(':') + 1)
const preview = this.previewEditedCost(row, editingProp)
if (preview && Object.prototype.hasOwnProperty.call(preview, columnProp)) {
return preview[columnProp]
}
return row[columnProp]
},
previewEditedCost (row, editingProp) {
const normalized = this.normalizeCostDraft(editingProp, this.costEditDraft)
if (normalized.error) {
return null
}
const rate = this.getCostRateNumber()
if (editingProp === 'tpKctUsd') {
const qty = this.getRowSampleQty(row)
const sampleCostUsd = normalized.value == null || qty == null
? null
: this.roundCost(normalized.value * qty / 1000)
return {
sampleCostUsd: sampleCostUsd,
sampleAndToolingCostUsd: this.sumPreviewCost(sampleCostUsd, this.toCostNumber(row.toolingCostUsd))
}
}
if (editingProp === 'toolingCostCn') {
const toolingCostUsd = normalized.value == null || rate == null
? null
: this.roundCost(normalized.value / rate)
return {
toolingCostUsd: toolingCostUsd,
sampleAndToolingCostUsd: this.sumPreviewCost(this.toCostNumber(row.sampleCostUsd), toolingCostUsd)
}
}
if (editingProp === 'sampleCostFromFnCn') {
return {
sampleCostFromFnUsd: normalized.value == null || rate == null
? null
: this.roundCost(normalized.value / rate)
}
}
return null
},
getCostCalculatedTitle (row, columnProp) {
const rate = this.formatCostDisplay(this.usdCnyExchangeRate)
const rateText = rate ? `汇率 ${rate}` : '未设置汇率,不能折算'
if (columnProp === 'sampleCostUsd') {
return '修改 TP 时按 TP(KCT/USD) × Sample Qty / 1000 计算'
}
if (columnProp === 'toolingCostUsd') {
return `修改 Tooling cost/CN 时按人民币 / ${rateText}`
}
if (columnProp === 'sampleAndToolingCostUsd') {
return 'Sample Cost/USD + Tooling Cost/USD。点“按汇率重算”会用当前汇率重算全部自动计算列'
}
if (columnProp === 'sampleCostFromFnUsd') {
return `修改 Sample Cost from FN/CN 时按人民币 / ${rateText}`
}
return ''
},
normalizeExchangeRateDraft (rawValue) {
const text = rawValue == null ? '' : String(rawValue).trim()
if (!text) {
return { value: null, error: '' }
}
if (!/^\d+(\.\d{1,6})?$/.test(text)) {
return { value: null, error: '汇率请输入大于0的数字,最多6位小数' }
}
const number = Number(text)
if (!Number.isFinite(number) || number <= 0 || number >= 1000) {
return { value: null, error: '汇率必须大于0且小于1000' }
}
return { value: Math.round((number + Number.EPSILON) * 1000000) / 1000000, error: '' }
},
isSameExchangeRate (left, right) {
if (left == null || right == null) {
return left == null && right == null
}
return Math.abs(Number(left) - Number(right)) < 0.0000005
},
// 汇率写在 siteid=2。空值允许保存,但之后不能把人民币折成美元。
saveExchangeRate () {
const normalized = this.normalizeExchangeRateDraft(this.usdCnyExchangeRateDraft)
if (normalized.error) {
this.$message.warning(normalized.error)
return Promise.resolve(false)
}
if (this.isSameExchangeRate(normalized.value, this.toCostNumber(this.usdCnyExchangeRate))) {
return Promise.resolve(true)
}
if (this._exchangeRateSavePromise) {
return this._exchangeRateSavePromise
}
this.costRateSaving = true
this._exchangeRateSavePromise = saveProofTrackingExchangeRate({
usdCnyRate: normalized.value,
updateBy: this.$store.state.user.name
}).then(({ data }) => {
if (!data || data.code !== 0) {
this.$message.error((data && data.msg) || '汇率保存失败')
return false
}
this.usdCnyExchangeRate = data.usdCnyExchangeRate == null ? null : data.usdCnyExchangeRate
this.usdCnyExchangeRateDraft = this.usdCnyExchangeRate == null ? '' : this.formatCostDisplay(this.usdCnyExchangeRate)
return true
}).catch(() => {
this.$message.error('汇率保存失败')
return false
}).finally(() => {
this.costRateSaving = false
this._exchangeRateSavePromise = null
})
return this._exchangeRateSavePromise
},
recalculateCostByRate () {
const normalized = this.normalizeExchangeRateDraft(this.usdCnyExchangeRateDraft)
if (normalized.error) {
this.$message.warning(normalized.error)
return
}
if (normalized.value == null) {
this.$message.warning('未设置汇率,无法计算')
return
}
this.$confirm('将按当前汇率重算全部自动计算的费用列并保存,是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.costRateRecalcLoading = true
return this.saveExchangeRate()
}).then(saved => {
if (saved !== true) {
return null
}
return refreshProofTrackingCostByRate({ updateBy: this.$store.state.user.name })
}).then(response => {
if (!response) {
return
}
const data = response.data
if (data && data.code === 0) {
this.$message.success(data.msg || '费用已按汇率重算')
this.getDataList()
return
}
this.$message.error((data && data.msg) || '按汇率重算失败')
}).catch(error => {
if (error === 'cancel' || error === 'close') {
return
}
this.$message.error('按汇率重算失败')
}).finally(() => {
this.costRateRecalcLoading = false
})
},
resolveCostEditor () {
let editor = this.$refs.costEditor
if (Array.isArray(editor)) {
editor = editor.filter(item => item)[0]
}
return editor || null
},
focusCostEditor (retried) {
const editor = this.resolveCostEditor()
if (!editor) {
if (!retried) {
this.$nextTick(() => {
this.focusCostEditor(true)
})
}
return
}
if (typeof editor.focus === 'function') {
editor.focus()
}
if (typeof editor.toggleMenu === 'function' && !editor.visible) {
editor.toggleMenu()
}
const input = editor.$refs && editor.$refs.input
if (input && typeof input.select === 'function' && typeof editor.toggleMenu !== 'function') {
input.select()
}
},
findCostEditingTarget () {
if (!this.costEditingKey) {
return null
}
const splitIndex = this.costEditingKey.indexOf(':')
if (splitIndex < 0) {
return null
}
const trackingId = this.costEditingKey.slice(0, splitIndex)
const columnProp = this.costEditingKey.slice(splitIndex + 1)
const row = (this.dataList || []).find(item => item && String(item.trackingId) === String(trackingId))
if (!row) {
return null
}
return { row: row, columnProp: columnProp }
},
exitCostEdit () {
this.costEditingKey = ''
this.costEditDraft = ''
this.costEditOriginal = ''
this.costSelectReady = false
},
cancelCostEdit (row) {
if (row && !this.isCostEditingRow(row)) {
return
}
this.exitCostEdit()
},
startCostEdit (row, columnProp) {
if (!row || !row.trackingId || !this.isCostManualColumn(columnProp)) {
return
}
const nextKey = `${row.trackingId}:${columnProp}`
if (this.costEditingKey === nextKey) {
return
}
if (this.costEditingKey) {
const editing = this.findCostEditingTarget()
if (editing) {
this.saveCostEdit(editing.row, editing.columnProp)
} else {
this.exitCostEdit()
}
}
this.costEditingKey = nextKey
this.costEditOriginal = this.getCostManualDisplay(row, columnProp)
this.costEditDraft = this.costEditOriginal
this.costSelectReady = false
this.$nextTick(() => {
this.focusCostEditor()
// 下拉刚挂上时会先关一次面板,这段时间不把关闭当成保存。
setTimeout(() => {
if (this.costEditingKey === nextKey) {
this.costSelectReady = true
}
}, 200)
})
},
handleCostSelectVisible (visible, row, columnProp) {
if (visible || !this.isCostEditing(row, columnProp) || !this.costSelectReady) {
return
}
const editor = this.resolveCostEditor()
const typed = editor && editor.query != null ? String(editor.query).trim() : ''
if (typed && typed !== String(this.costEditDraft || '').trim()) {
this.costEditDraft = typed
}
this.saveCostEdit(row, columnProp)
},
saveCostEdit (row, columnProp) {
if (!this.isCostEditing(row, columnProp)) {
return
}
const normalized = this.normalizeCostDraft(columnProp, this.costEditDraft)
if (normalized.error) {
this.$message.warning(normalized.error)
return
}
if (this.isSameCostDraft(columnProp, normalized.value, this.costEditOriginal)) {
this.exitCostEdit()
return
}
const version = (row._costSaveVersion || 0) + 1
this.$set(row, '_costSaveVersion', version)
this.$set(row, columnProp, normalized.value)
const payload = {
trackingId: row.trackingId,
customizedForMasAls: this.isBlankValue(row.customizedForMasAls) ? null : String(row.customizedForMasAls).trim(),
tpKctUsd: this.toCostNumber(row.tpKctUsd),
toolingCostCn: this.toCostNumber(row.toolingCostCn),
sampleCostFromFnCn: this.toCostNumber(row.sampleCostFromFnCn),
updateBy: this.$store.state.user.name
}
this.exitCostEdit()
updateProofTrackingCost(payload).then(({ data }) => {
const current = (this.dataList || []).find(item => item && item.trackingId === row.trackingId)
if (!current || (current._costSaveVersion != null && current._costSaveVersion !== version)) {
return
}
if (data && data.code === 0) {
this.applySavedCost(current, data.row || {})
if (data.msg && String(data.msg).indexOf('未设置汇率') > -1) {
this.$message.warning(data.msg)
}
return
}
this.$message.error((data && data.msg) || '费用保存失败')
this.getDataList()
}).catch(() => {
const current = (this.dataList || []).find(item => item && item.trackingId === row.trackingId)
if (!current || (current._costSaveVersion != null && current._costSaveVersion !== version)) {
return
}
this.$message.error('费用保存异常')
this.getDataList()
})
},
applySavedCost (row, saved) {
const fields = [
'customizedForMasAls',
'tpKctUsd',
'toolingCostCn',
'sampleCostFromFnCn',
'sampleCostUsd',
'toolingCostUsd',
'sampleAndToolingCostUsd',
'sampleCostFromFnUsd'
]
fields.forEach(field => {
if (saved && Object.prototype.hasOwnProperty.call(saved, field)) {
this.$set(row, field, saved[field])
}
})
},
handleTrackingTableSortChange (sortData) { handleTrackingTableSortChange (sortData) {
this.trackingTableSortProp = sortData && sortData.prop ? String(sortData.prop) : '' this.trackingTableSortProp = sortData && sortData.prop ? String(sortData.prop) : ''
this.trackingTableSortOrder = sortData && sortData.order ? String(sortData.order) : '' this.trackingTableSortOrder = sortData && sortData.order ? String(sortData.order) : ''
@ -5727,8 +6360,7 @@ export default {
baseline: current.baseline || '', baseline: current.baseline || '',
pmInqueryTime: this.formatDate(current.pmInqueryTime), pmInqueryTime: this.formatDate(current.pmInqueryTime),
importance: this.normalizeImportanceValue(current.importance), importance: this.normalizeImportanceValue(current.importance),
needDate: project.needDate || part.needDate || current.needDate || '',
remark: part.remark || project.remark || current.remark || ''
needDate: project.needDate || part.needDate || current.needDate || ''
}) })
form.projectManagerName = this.getRoleDisplayName(form.projectManager) form.projectManagerName = this.getRoleDisplayName(form.projectManager)
form.projectOwnerName = this.getRoleDisplayName(form.projectOwner) form.projectOwnerName = this.getRoleDisplayName(form.projectOwner)
@ -5855,9 +6487,9 @@ export default {
: '' : ''
const nextStatus = inData.proofingStatus ? String(inData.proofingStatus).trim() : '' const nextStatus = inData.proofingStatus ? String(inData.proofingStatus).trim() : ''
const proofingStatusChanged = this.oneKeyDialogMode !== 'create' && nextStatus !== previousStatus const proofingStatusChanged = this.oneKeyDialogMode !== 'create' && nextStatus !== previousStatus
if (proofingStatusChanged && this.proofingStatusChangeOptions.indexOf(nextStatus) === -1) {
if (proofingStatusChanged && !this.isAllowedOneKeyProofingStatusChange(previousStatus, nextStatus)) {
this.saveOneKeyLoading = false this.saveOneKeyLoading = false
this.$message.warning('打样状态只能更改为终止、取消、报废')
this.$message.warning('打样状态只能更改为终止、取消、报废;当前为终止、取消、报废时可以改回进行中')
return return
} }
if (proofingStatusChanged) { if (proofingStatusChanged) {
@ -6483,7 +7115,23 @@ export default {
return '#046e97' return '#046e97'
}, },
isProofingClosedStatus (row) { isProofingClosedStatus (row) {
return this.proofingStatusChangeOptions.indexOf(this.getNormalizedProofingStatus(row)) > -1
return this.isClosedProofingStatusValue(this.getNormalizedProofingStatus(row))
},
isClosedProofingStatusValue (status) {
const normalizedStatus = status ? String(status).trim() : ''
return this.proofingStatusChangeOptions.indexOf(normalizedStatus) > -1
},
isAllowedOneKeyProofingStatusChange (previousStatus, nextStatus) {
const previous = previousStatus ? String(previousStatus).trim() : ''
const next = nextStatus ? String(nextStatus).trim() : ''
if (!next || next === previous) {
return true
}
if (this.proofingStatusChangeOptions.indexOf(next) > -1) {
return true
}
// 重新打开只允许从终止/取消/报废回到进行中,避免草稿等状态被改成进行中。
return next === '进行中' && this.isClosedProofingStatusValue(previous)
}, },
canDeleteProofStatus (row) { canDeleteProofStatus (row) {
return this.deletableProofingStatuses.indexOf(this.getNormalizedProofingStatus(row)) > -1 return this.deletableProofingStatuses.indexOf(this.getNormalizedProofingStatus(row)) > -1
@ -7630,6 +8278,50 @@ export default {
min-width: 28px; min-width: 28px;
} }
.cost-rate-tip {
display: inline-flex;
align-items: center;
margin-left: 10px;
color: #606266;
font-size: 12px;
line-height: 18px;
white-space: nowrap;
}
.cost-rate-input {
width: 120px;
margin-left: 6px;
}
.cost-cell {
min-height: 24px;
}
.cost-display--editable {
display: inline-block;
width: 100%;
min-height: 24px;
line-height: 24px;
padding: 0 4px;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
box-sizing: border-box;
}
.cost-display--editable:hover {
border-color: #dcdfe6;
background: #fff;
}
.el-table.proof-tracking-table .cost-editor {
width: 100%;
}
.cost-calculated-text {
color: #606266;
}
/* Comments 两行共 28px,与原单元格内容高度一致,不撑高整行 */ /* Comments 两行共 28px,与原单元格内容高度一致,不撑高整行 */
.el-table.proof-tracking-table .comments-text { .el-table.proof-tracking-table .comments-text {
display: -webkit-box; display: -webkit-box;

Loading…
Cancel
Save