| | |
| | | </div> |
| | | |
| | | <el-form :model="form" label-width="120px" :rules="rules" ref="formRef"> |
| | | <div style="width: 350px; margin-bottom: 12px; margin-left: 120px;"> |
| | | <el-select |
| | | v-model="selectedEngineeringId" |
| | | placeholder="选择工程号(选择后自动填充玻璃ID)" |
| | | clearable |
| | | filterable |
| | | :disabled="!group" |
| | | :loading="engineeringListLoading" |
| | | @change="handleEngineeringChange" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in engineeringList" |
| | | :key="item.engineeringId" |
| | | :label="item.engineeringId" |
| | | :value="item.engineeringId" |
| | | > |
| | | <div style="display: flex; justify-content: space-between; align-items: center; width: 100%;"> |
| | | <div style="flex: 1;"> |
| | | <span>{{ item.engineeringId }}</span> |
| | | <span style="margin-left: 8px; color: #8492a6; font-size: 12px"> |
| | | {{ item.date ? new Date(item.date).toLocaleDateString() : '' }} |
| | | </span> |
| | | </div> |
| | | <el-button |
| | | type="danger" |
| | | link |
| | | size="small" |
| | | :loading="deletingEngineeringId === item.engineeringId" |
| | | @click.stop="handleDeleteEngineering(item.engineeringId)" |
| | | style="margin-left: 8px; padding: 0 4px;" |
| | | > |
| | | <el-icon><Delete /></el-icon> |
| | | </el-button> |
| | | </div> |
| | | </el-option> |
| | | </el-select> |
| | | </div> |
| | | |
| | | <el-form-item label="玻璃ID列表" prop="glassIds"> |
| | | <el-input |
| | | v-model="glassIdsInput" |
| | | type="textarea" |
| | | :rows="4" |
| | | placeholder="可选:输入玻璃ID,将使用输入的ID进行测试" |
| | | placeholder="可选:输入玻璃ID,将使用输入的ID进行测试(或通过上方选择工程号自动填充)" |
| | | show-word-limit |
| | | :maxlength="5000" |
| | | /> |
| | |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed, reactive, ref, watch } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { computed, reactive, ref, watch, onMounted } from 'vue' |
| | | import { ElMessage, ElMessageBox } from 'element-plus' |
| | | import { Delete, Promotion, Upload } from '@element-plus/icons-vue' |
| | | import * as XLSX from 'xlsx' |
| | | import { multiDeviceTaskApi } from '@/api/device/multiDeviceTask' |
| | |
| | | const loadDeviceLoading = ref(false) |
| | | const fileInputRef = ref(null) |
| | | |
| | | // 工程号相关 |
| | | const selectedEngineeringId = ref('') |
| | | const engineeringList = ref([]) |
| | | const engineeringListLoading = ref(false) |
| | | const glassIdsLoading = ref(false) |
| | | const deletingEngineeringId = ref('') |
| | | |
| | | watch( |
| | | () => props.group, |
| | | () => { |
| | | glassIdsInput.value = '' |
| | | selectedEngineeringId.value = '' |
| | | fetchLoadDevice() |
| | | fetchEngineeringList() |
| | | } |
| | | ) |
| | | |
| | | // 组件挂载时加载工程号列表 |
| | | onMounted(() => { |
| | | fetchEngineeringList() |
| | | }) |
| | | |
| | | const glassIds = computed(() => { |
| | | if (!glassIdsInput.value) return [] |
| | |
| | | .map((item) => item.trim()) |
| | | .filter((item) => item.length > 0) |
| | | }) |
| | | |
| | | // 获取工程号列表 |
| | | const fetchEngineeringList = async () => { |
| | | try { |
| | | engineeringListLoading.value = true |
| | | const response = await engineeringApi.getEngineeringList() |
| | | |
| | | if (Array.isArray(response)) { |
| | | engineeringList.value = response |
| | | } else if (Array.isArray(response?.data)) { |
| | | engineeringList.value = response.data |
| | | } else { |
| | | engineeringList.value = [] |
| | | } |
| | | // 按日期倒序排列 |
| | | engineeringList.value.sort((a, b) => { |
| | | const dateA = a.date ? new Date(a.date).getTime() : 0 |
| | | const dateB = b.date ? new Date(b.date).getTime() : 0 |
| | | return dateB - dateA |
| | | }) |
| | | } catch (error) { |
| | | console.error('获取工程号列表失败:', error) |
| | | ElMessage.error(error?.message || '获取工程号列表失败') |
| | | engineeringList.value = [] |
| | | } finally { |
| | | engineeringListLoading.value = false |
| | | } |
| | | } |
| | | |
| | | // 处理工程号选择变化 |
| | | const handleEngineeringChange = async (engineeringId) => { |
| | | if (!engineeringId) { |
| | | // 清空选择时,不清空已输入的玻璃ID,让用户保留 |
| | | return |
| | | } |
| | | |
| | | try { |
| | | glassIdsLoading.value = true |
| | | const response = await engineeringApi.getGlassIdsByEngineeringId(engineeringId) |
| | | |
| | | const glassIds = response?.glassIds || response?.data?.glassIds || [] |
| | | |
| | | if (glassIds.length > 0) { |
| | | glassIdsInput.value = glassIds.join('\n') |
| | | ElMessage.success(`已加载工程号 ${engineeringId} 的 ${glassIds.length} 个玻璃ID`) |
| | | } else { |
| | | ElMessage.warning(`工程号 ${engineeringId} 下没有找到玻璃ID`) |
| | | } |
| | | } catch (error) { |
| | | console.error('获取玻璃ID列表失败:', error) |
| | | ElMessage.error(error?.message || '获取玻璃ID列表失败') |
| | | } finally { |
| | | glassIdsLoading.value = false |
| | | } |
| | | } |
| | | |
| | | // 处理删除工程号 |
| | | const handleDeleteEngineering = async (engineeringId) => { |
| | | if (!engineeringId) { |
| | | return |
| | | } |
| | | |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | `确定要删除工程号 "${engineeringId}" 及其关联的所有玻璃信息吗?此操作不可恢复!`, |
| | | '确认删除', |
| | | { |
| | | confirmButtonText: '确定删除', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | dangerouslyUseHTMLString: false |
| | | } |
| | | ) |
| | | |
| | | deletingEngineeringId.value = engineeringId |
| | | const response = await engineeringApi.deleteEngineering(engineeringId) |
| | | |
| | | const result = response?.data || response |
| | | if (result?.success !== false) { |
| | | const deletedCount = result?.deletedGlassCount || 0 |
| | | ElMessage.success(`已删除工程号 ${engineeringId},共删除 ${deletedCount} 条玻璃信息`) |
| | | |
| | | // 如果删除的是当前选中的工程号,清空选择 |
| | | if (selectedEngineeringId.value === engineeringId) { |
| | | selectedEngineeringId.value = '' |
| | | glassIdsInput.value = '' |
| | | } |
| | | |
| | | // 刷新工程号列表 |
| | | await fetchEngineeringList() |
| | | } else { |
| | | throw new Error(result?.message || '删除失败') |
| | | } |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | console.error('删除工程号失败:', error) |
| | | ElMessage.error(error?.message || '删除工程号失败') |
| | | } |
| | | } finally { |
| | | deletingEngineeringId.value = '' |
| | | } |
| | | } |
| | | |
| | | const normalizeType = (type) => (type || '').trim().toUpperCase() |
| | | |
| | |
| | | headerStr === 'w' || headerStr === '宽度') { |
| | | headerMap.width = index |
| | | } |
| | | // 高度 |
| | | else if (headerStr.includes('高') || headerStr.includes('height') || |
| | | headerStr === 'h' || headerStr === '高度') { |
| | | headerMap.height = index |
| | | // 长度 |
| | | else if (headerStr.includes('长') || headerStr.includes('length') || |
| | | headerStr === 'l' || headerStr === '长度') { |
| | | headerMap.length = index |
| | | } |
| | | // 厚度 |
| | | else if (headerStr.includes('厚') || headerStr.includes('thickness') || |
| | |
| | | |
| | | // 如果没有找到表头,尝试使用第一行作为表头(索引方式) |
| | | if (Object.keys(headerMap).length === 0 && jsonData.length > 1) { |
| | | // 默认格式:玻璃ID, 宽, 高, 厚, 数量(按列顺序) |
| | | // 默认格式:玻璃ID, 宽, 长, 厚, 数量(按列顺序) |
| | | headerMap.glassId = 0 |
| | | headerMap.width = 1 |
| | | headerMap.height = 2 |
| | | headerMap.length = 2 |
| | | headerMap.thickness = 3 |
| | | headerMap.quantity = 4 |
| | | } |
| | |
| | | |
| | | const glassId = row[headerMap.glassId] ? String(row[headerMap.glassId]).trim() : '' |
| | | const width = row[headerMap.width] ? String(row[headerMap.width]).trim() : '' |
| | | const height = row[headerMap.height] ? String(row[headerMap.height]).trim() : '' |
| | | const length = row[headerMap.length] ? String(row[headerMap.length]).trim() : '' |
| | | const thickness = row[headerMap.thickness] ? String(row[headerMap.thickness]).trim() : '' |
| | | const quantity = row[headerMap.quantity] ? String(row[headerMap.quantity]).trim() : '' |
| | | const filmsId = row[headerMap.filmsId] ? String(row[headerMap.filmsId]).trim() : '' |
| | |
| | | const customerName = row[headerMap.customerName] ? String(row[headerMap.customerName]).trim() : '' |
| | | |
| | | // 跳过空行 |
| | | if (!glassId && !width && !height && !thickness && !quantity) { |
| | | if (!glassId && !width && !length && !thickness && !quantity) { |
| | | continue |
| | | } |
| | | |
| | |
| | | const qty = parseInt(quantity) || 1 |
| | | for (let j = 0; j < qty; j++) { |
| | | // 如果数量大于1,为每条记录生成唯一的玻璃ID(追加序号) |
| | | const finalGlassId = qty > 1 ? `${glassId}_${j + 1}` : glassId |
| | | const finalGlassId = qty > 1 ? `${glassId}${j + 1}` : glassId |
| | | |
| | | result.push({ |
| | | glassId: finalGlassId, |
| | | width: parseNumber(width), |
| | | height: parseNumber(height), |
| | | length: parseNumber(length), |
| | | thickness: parseNumber(thickness), |
| | | quantity: '1', // 每条记录数量为1 |
| | | filmsId: filmsId, |
| | |
| | | : `成功导入 ${glassDataList.length} 条玻璃数据` |
| | | ElMessage.success(successMsg) |
| | | |
| | | // 将导入的玻璃ID填充到输入框,方便用户查看和编辑 |
| | | const glassIds = glassDataList.map(item => item.glassId).filter(id => id) |
| | | if (glassIds.length > 0) { |
| | | glassIdsInput.value = glassIds.join('\n') |
| | | // 成功后刷新工程号下拉列表,并选中最新工程号 |
| | | try { |
| | | await fetchEngineeringList() |
| | | if (engineerId) { |
| | | selectedEngineeringId.value = engineerId |
| | | // 刷新并回填后端保存的 glassId(带工程号前缀),避免使用前端原始值 |
| | | await handleEngineeringChange(engineerId) |
| | | } |
| | | } catch (refreshErr) { |
| | | console.error('刷新工程号列表失败:', refreshErr) |
| | | } |
| | | } else { |
| | | // MES 接口返回失败 |