2026-06-22 22:44:45 +08:00
|
|
|
<template>
|
|
|
|
|
<el-drawer :visible.sync="visible" :wrapper-closable="false" :with-header="false" size="50%">
|
|
|
|
|
<div class="drawer-title">
|
2026-06-25 00:46:42 +08:00
|
|
|
<el-page-header @back="handleClose" :content="title" />
|
2026-06-22 22:44:45 +08:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="section">
|
|
|
|
|
<div class="section-title">| {{ $t(key('temperature_step_range')) }}</div>
|
|
|
|
|
<el-form :inline="true" :model="formTemp" size="mini">
|
|
|
|
|
<el-form-item :label="$t(key('start_step'))">
|
|
|
|
|
<el-select v-model="formTemp.start_work_step_id" :placeholder="$t(key('select_start_step'))" @change="checkStepRange">
|
|
|
|
|
<el-option v-for="item in stepOptions" :key="item.id" :label="item.suffix_name || item.name" :value="item.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item :label="$t(key('end_step'))">
|
|
|
|
|
<el-select v-model="formTemp.end_work_step_id" :placeholder="$t(key('select_end_step'))" @change="checkStepRange">
|
|
|
|
|
<el-option v-for="item in stepOptions" :key="item.id" :label="item.suffix_name || item.name" :value="item.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="section">
|
|
|
|
|
<div class="section-title">| {{ $t(key('temperature_data')) }}</div>
|
|
|
|
|
<div class="toolbar">
|
|
|
|
|
<el-button size="mini" icon="el-icon-plus" @click="addRow">{{ $t(key('add')) }}</el-button>
|
|
|
|
|
<el-button size="mini" icon="el-icon-delete" @click="removeRows">{{ $t(key('remove')) }}</el-button>
|
|
|
|
|
<el-button size="mini" type="primary" icon="el-icon-check" @click="saveRows">{{ $t(key('save')) }}</el-button>
|
|
|
|
|
<el-button size="mini" icon="el-icon-upload2" @click="importVisible = true">{{ $t(key('import_data')) }}</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<el-table ref="table" :data="tableData" border height="560" @selection-change="selectedRows = $event">
|
|
|
|
|
<el-table-column type="selection" width="48" />
|
|
|
|
|
<el-table-column :label="$t(key('temperature_value'))" min-width="160">
|
|
|
|
|
<template slot-scope="{ row }">
|
|
|
|
|
<el-input v-model="row.temperature" size="mini" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
<el-table-column :label="$t(key('compensation_value'))" min-width="160">
|
|
|
|
|
<template slot-scope="{ row }">
|
|
|
|
|
<el-input v-model="row.compensation_value" size="mini" />
|
|
|
|
|
</template>
|
|
|
|
|
</el-table-column>
|
|
|
|
|
</el-table>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<el-dialog :title="$t(key('import_temperature_data'))" :visible.sync="importVisible" append-to-body width="420px">
|
|
|
|
|
<el-upload
|
|
|
|
|
drag
|
|
|
|
|
action=""
|
|
|
|
|
:auto-upload="false"
|
|
|
|
|
:show-file-list="false"
|
|
|
|
|
accept=".xls,.xlsx"
|
|
|
|
|
:on-change="onFileChange"
|
|
|
|
|
>
|
|
|
|
|
<i class="el-icon-upload" />
|
|
|
|
|
<div class="el-upload__text">{{ $t(key('drag_file_here')) }}<em>{{ $t(key('click_to_upload')) }}</em></div>
|
|
|
|
|
<div slot="tip" class="el-upload__tip">{{ fileName }}</div>
|
|
|
|
|
</el-upload>
|
|
|
|
|
<span slot="footer">
|
|
|
|
|
<el-button type="primary" :loading="templateLoading" @click="downloadTemplate">{{ $t(key('download_template')) }}</el-button>
|
|
|
|
|
</span>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</el-drawer>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { i18nMixin } from '@/composables/useI18n'
|
|
|
|
|
import { getStep, getTemperatureList, createTemperature, getTemperatureTemplate } from '@/api/production-master-data/process-routing-card'
|
|
|
|
|
import { downloadRename, readExcel } from '@/utils/file'
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'ProcessRoutingCardTemperatureCompensation',
|
|
|
|
|
mixins: [i18nMixin('page.production_master_data.process_model.process_routing.card')],
|
|
|
|
|
props: {
|
|
|
|
|
title: { type: String, default: '' },
|
|
|
|
|
visible: { type: Boolean, default: false },
|
|
|
|
|
flowProcessId: { type: [String, Number], default: '' }
|
|
|
|
|
},
|
|
|
|
|
data () {
|
|
|
|
|
return {
|
|
|
|
|
formTemp: { start_work_step_id: '', end_work_step_id: '' },
|
|
|
|
|
stepOptions: [],
|
|
|
|
|
tableData: [],
|
|
|
|
|
selectedRows: [],
|
|
|
|
|
importVisible: false,
|
|
|
|
|
fileName: '',
|
|
|
|
|
templateLoading: false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
visible (val) {
|
|
|
|
|
if (val) this.bootstrap()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
handleClose () {
|
|
|
|
|
this.$emit('close')
|
|
|
|
|
},
|
|
|
|
|
async bootstrap () {
|
|
|
|
|
await Promise.all([this.loadSteps(), this.loadTemperatureList()])
|
|
|
|
|
},
|
|
|
|
|
async loadSteps () {
|
|
|
|
|
const res = await getStep({})
|
|
|
|
|
this.stepOptions = (res && res.data) || res || []
|
|
|
|
|
},
|
|
|
|
|
async loadTemperatureList () {
|
|
|
|
|
if (!this.flowProcessId) return
|
|
|
|
|
const res = await getTemperatureList({ process_id: this.flowProcessId })
|
|
|
|
|
const data = (res && res.data) || res || {}
|
|
|
|
|
this.tableData = data.data || []
|
|
|
|
|
this.formTemp.start_work_step_id = data.start_work_step_id || ''
|
|
|
|
|
this.formTemp.end_work_step_id = data.end_work_step_id || ''
|
|
|
|
|
},
|
|
|
|
|
checkStepRange () {
|
|
|
|
|
const start = Number(this.formTemp.start_work_step_id)
|
|
|
|
|
const end = Number(this.formTemp.end_work_step_id)
|
|
|
|
|
if (start && end && start > end) this.$message.warning(this.$t(this.key('start_step_greater_than_end')))
|
|
|
|
|
},
|
|
|
|
|
addRow () {
|
|
|
|
|
this.tableData.push({ temperature: '', compensation_value: '' })
|
|
|
|
|
},
|
|
|
|
|
removeRows () {
|
|
|
|
|
if (!this.selectedRows.length) {
|
|
|
|
|
this.$message.error(this.$t(this.key('select_at_least_one')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.tableData = this.tableData.filter(row => !this.selectedRows.includes(row))
|
|
|
|
|
},
|
|
|
|
|
validateRows () {
|
|
|
|
|
if (!this.formTemp.start_work_step_id) return this.$t(this.key('select_start_step_required'))
|
|
|
|
|
if (!this.formTemp.end_work_step_id) return this.$t(this.key('select_end_step_required'))
|
|
|
|
|
const seen = {}
|
|
|
|
|
for (const row of this.tableData) {
|
|
|
|
|
if (row.temperature === '' || row.temperature === null || row.temperature === undefined) return this.$t(this.key('temperature_required'))
|
|
|
|
|
if (row.compensation_value === '' || row.compensation_value === null || row.compensation_value === undefined) return this.$t(this.key('compensation_required'))
|
|
|
|
|
if (!/^[-]?\d+(\.\d+)?$/.test(String(row.temperature))) return this.$t(this.key('temperature_must_be_numeric'))
|
|
|
|
|
if (!/^[-]?\d+(\.\d+)?$/.test(String(row.compensation_value))) return this.$t(this.key('compensation_must_be_numeric'))
|
|
|
|
|
if (seen[row.temperature]) return this.$t(this.key('duplicate_temperature_exists'))
|
|
|
|
|
seen[row.temperature] = true
|
|
|
|
|
}
|
|
|
|
|
return ''
|
|
|
|
|
},
|
|
|
|
|
async saveRows () {
|
|
|
|
|
const error = this.validateRows()
|
|
|
|
|
if (error) {
|
|
|
|
|
this.$message.error(error)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
await createTemperature({
|
|
|
|
|
temp_data: this.tableData,
|
|
|
|
|
start_work_step_id: this.formTemp.start_work_step_id,
|
|
|
|
|
end_work_step_id: this.formTemp.end_work_step_id,
|
|
|
|
|
process_id: this.flowProcessId
|
|
|
|
|
})
|
|
|
|
|
this.$message.success(this.$t(this.key('operation_success')))
|
|
|
|
|
this.loadTemperatureList()
|
|
|
|
|
},
|
|
|
|
|
async downloadTemplate () {
|
|
|
|
|
this.templateLoading = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await getTemperatureTemplate({})
|
|
|
|
|
downloadRename(res, 'xlsx', this.$t(this.key('temperature_template_name')))
|
|
|
|
|
} finally {
|
|
|
|
|
this.templateLoading = false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async onFileChange (file) {
|
|
|
|
|
if (!file || !/\.(xls|xlsx)$/i.test(file.name)) {
|
|
|
|
|
this.$message.error(this.$t(this.key('invalid_upload_format')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.fileName = file.name
|
|
|
|
|
const rows = await readExcel(file.raw)
|
|
|
|
|
const next = [...this.tableData]
|
|
|
|
|
for (const row of rows) {
|
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(row, '温度') || !Object.prototype.hasOwnProperty.call(row, '温度补偿值')) {
|
|
|
|
|
this.$message.error(this.$t(this.key('temperature_import_columns_required')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (next.some(item => String(item.temperature) === String(row['温度']))) {
|
|
|
|
|
this.$message.error(this.$t(this.key('duplicate_temperature_exists')))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
next.push({ temperature: row['温度'], compensation_value: row['温度补偿值'] })
|
|
|
|
|
}
|
|
|
|
|
this.tableData = next.sort((a, b) => Number(a.temperature) - Number(b.temperature))
|
|
|
|
|
this.importVisible = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.drawer-title {
|
|
|
|
|
padding: 20px 0 20px 20px;
|
|
|
|
|
border-bottom: 1px solid #dcdfe6;
|
|
|
|
|
}
|
|
|
|
|
.section {
|
|
|
|
|
margin: 20px;
|
|
|
|
|
}
|
|
|
|
|
.section-title {
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
.toolbar {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
</style>
|