From a582fffa7d3f7283e809a7940538ab02e4677948 Mon Sep 17 00:00:00 2001
From: spring <2396852758@qq.com>
Date: 星期五, 24 四月 2026 09:55:21 +0800
Subject: [PATCH] Merge branch 'dev_NEW_pro' of http://114.132.189.42:9002/r/product-inventory-management into dev_NEW_pro
---
src/api/basicData/common.js | 13
src/components/AttachmentUpload/image/index.vue | 335 ++++++++++++
src/components/AttachmentPreview/image/index.vue | 76 ++
src/components/AttachmentUpload/file/index.vue | 309 +++++++++++
src/api/productionManagement/productBom.js | 8
src/views/productionManagement/productStructure/index.vue | 869 ++++++++++++++++++--------------
6 files changed, 1,227 insertions(+), 383 deletions(-)
diff --git a/src/api/basicData/common.js b/src/api/basicData/common.js
new file mode 100644
index 0000000..01c6f35
--- /dev/null
+++ b/src/api/basicData/common.js
@@ -0,0 +1,13 @@
+import request from '@/utils/request'
+
+// 閫氱敤涓婁紶鎺ュ彛锛屾敮鎸� FormData 鎵归噺浼犳枃浠�
+export function uploadFile(data) {
+ return request({
+ url: '/common/upload',
+ method: 'post',
+ data,
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ })
+}
diff --git a/src/api/productionManagement/productBom.js b/src/api/productionManagement/productBom.js
index 097d223..517208b 100644
--- a/src/api/productionManagement/productBom.js
+++ b/src/api/productionManagement/productBom.js
@@ -19,6 +19,14 @@
});
}
+// 澶嶅埗
+export function copy(data) {
+ return request({
+ url: "/technologyBom/copy",
+ method: "post",
+ data: data,
+ });
+}
// 淇敼
export function update(data) {
return request({
diff --git a/src/components/AttachmentPreview/image/index.vue b/src/components/AttachmentPreview/image/index.vue
new file mode 100644
index 0000000..5211fac
--- /dev/null
+++ b/src/components/AttachmentPreview/image/index.vue
@@ -0,0 +1,76 @@
+<script setup>
+const props = defineProps({
+ list: {
+ type: Array,
+ default: () => [],
+ },
+ thumbSize: {
+ type: Number,
+ default: 72,
+ },
+ gap: {
+ type: Number,
+ default: 10,
+ },
+})
+
+const normalizedList = computed(() => {
+ return (props.list || [])
+ .filter((item) => item && item.previewURL)
+ .map((item, index) => ({
+ id: item.id ?? index,
+ name: item.originalFilename || `image-${index + 1}`,
+ url: item.previewURL,
+ }))
+})
+const previewUrls = computed(() => normalizedList.value.map((item) => item.url))
+</script>
+
+<template>
+ <div class="attachment-image-preview">
+ <div v-if="!normalizedList.length" class="empty">鏆傛棤鍥剧墖</div>
+
+ <div v-else class="thumbs" :style="{ gap: `${gap}px` }">
+ <el-image
+ v-for="(item, index) in normalizedList"
+ :key="item.id"
+ class="thumb"
+ :style="{ width: `${thumbSize}px`, height: `${thumbSize}px` }"
+ :src="item.url"
+ :preview-src-list="previewUrls"
+ :initial-index="index"
+ fit="cover"
+ preview-teleported
+ />
+ </div>
+ </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-image-preview {
+ width: 100%;
+}
+
+.empty {
+ height: 120px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--el-text-color-secondary);
+ border: 1px dashed var(--el-border-color);
+ border-radius: 8px;
+}
+
+.thumbs {
+ display: flex;
+ flex-wrap: wrap;
+}
+
+.thumb {
+ border: 1px solid var(--el-border-color);
+ border-radius: 6px;
+ overflow: hidden;
+ cursor: pointer;
+ background: #fff;
+}
+</style>
diff --git a/src/components/AttachmentUpload/file/index.vue b/src/components/AttachmentUpload/file/index.vue
new file mode 100644
index 0000000..b68bd4f
--- /dev/null
+++ b/src/components/AttachmentUpload/file/index.vue
@@ -0,0 +1,309 @@
+<script setup>
+import { UploadFilled } from '@element-plus/icons-vue'
+import { uploadFile } from '@/api/basicData/common'
+
+const props = defineProps({
+ fileList: {
+ type: Array,
+ default: () => [],
+ },
+ index: {
+ type: Number,
+ default: -1,
+ },
+ childrenKey: {
+ type: String,
+ default: 'files',
+ },
+ limit: {
+ type: Number,
+ default: 20,
+ },
+ fileSize: {
+ type: Number,
+ default: 50,
+ },
+ fileType: {
+ type: Array,
+ default: () => [],
+ },
+ buttonText: {
+ type: String,
+ default: '鍗曞嚮閫夋嫨鏂囦欢',
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ uploadFieldName: {
+ type: String,
+ default: 'files',
+ },
+})
+
+const emit = defineEmits(['update:fileList', 'change'])
+const { proxy } = getCurrentInstance()
+
+const uploadRef = ref()
+const uploadQueueTimer = ref(null)
+const uploading = ref(false)
+const queuedUidSet = ref(new Set())
+const innerList = ref([])
+
+function readListFromProps() {
+ if (props.index > -1) {
+ const row = props.fileList?.[props.index]
+ return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : []
+ }
+ return Array.isArray(props.fileList) ? props.fileList : []
+}
+
+watch(
+ () => props.fileList,
+ () => {
+ innerList.value = [...readListFromProps()]
+ },
+ { deep: true, immediate: true },
+)
+
+const currentList = computed({
+ get() {
+ return innerList.value
+ },
+ set(value) {
+ const nextList = Array.isArray(value) ? value : []
+ innerList.value = nextList
+
+ if (props.index > -1) {
+ const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : []
+ const currentRow = nextModelValue[props.index] || {}
+ nextModelValue[props.index] = {
+ ...currentRow,
+ [props.childrenKey]: nextList,
+ }
+ emit('update:fileList', nextModelValue)
+ emit('change', nextList, nextModelValue)
+ return
+ }
+
+ emit('update:fileList', nextList)
+ emit('change', nextList, nextList)
+ },
+})
+
+const displayFileList = computed(() => {
+ return currentList.value.map((item, index) => ({
+ uid: getItemUid(item, index),
+ name: getItemName(item, index),
+ url: getItemUrl(item),
+ status: 'success',
+ rawData: item,
+ }))
+})
+
+const uploadTip = computed(() => {
+ if (!props.fileType.length) return `鍗曚釜鏂囦欢涓嶈秴杩� ${props.fileSize}MB`
+ return `鏀寔 ${props.fileType.join('/')}锛屽崟涓枃浠朵笉瓒呰繃 ${props.fileSize}MB`
+})
+
+function getItemUid(item, index) {
+ if (item?.id !== undefined && item?.id !== null) return `${item.id}`
+ return `${getItemName(item, index)}-${getItemUrl(item) || index}`
+}
+
+function getItemUrl(item) {
+ if (!item) return ''
+ if (typeof item === 'string') return item
+ return item.url || item.downloadURL || item.previewURL || item.previewUrl || ''
+}
+
+function getItemName(item, index = 0) {
+ if (!item) return `file-${index + 1}`
+ if (typeof item === 'string') return `file-${index + 1}`
+ return item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${index + 1}`
+}
+
+function normalizeResponseItem(item, index) {
+ if (typeof item === 'string') {
+ return {
+ name: `file-${currentList.value.length + index + 1}`,
+ url: item,
+ }
+ }
+ return Object.assign({}, item, {
+ url: item.url || item.downloadURL || item.previewURL || item.previewUrl || '',
+ name: item.name || item.originalFilename || item.fileName || item.uidFilename || `file-${currentList.value.length + index + 1}`,
+ })
+}
+
+function extractResponseArray(response) {
+ if (Array.isArray(response)) return response
+ if (Array.isArray(response?.data)) return response.data
+ if (Array.isArray(response?.data?.data)) return response.data.data
+ if (Array.isArray(response?.payload)) return response.payload
+ if (Array.isArray(response?.payload?.data)) return response.payload.data
+ if (Array.isArray(response?.rows)) return response.rows
+ if (Array.isArray(response?.result)) return response.result
+ return []
+}
+
+function validateFile(rawFile) {
+ const extension = rawFile.name.includes('.')
+ ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase()
+ : ''
+
+ if (props.fileType.length) {
+ const isValidType = props.fileType.some((type) => {
+ const normalizedType = String(type).toLowerCase()
+ return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType
+ })
+ if (!isValidType) {
+ proxy.$modal.msgError(`璇蜂笂浼� ${props.fileType.join('/')} 鏍煎紡鐨勬枃浠禶)
+ return false
+ }
+ }
+
+ const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize
+ if (!isWithinSize) {
+ proxy.$modal.msgError(`鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize}MB`)
+ return false
+ }
+
+ return true
+}
+
+function scheduleUpload(uploadFiles) {
+ clearTimeout(uploadQueueTimer.value)
+ uploadQueueTimer.value = setTimeout(() => {
+ const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid))
+ if (!readyFiles.length) return
+
+ const remainCount = props.limit - currentList.value.length
+ if (remainCount <= 0) {
+ proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 涓枃浠禶)
+ uploadRef.value?.clearFiles()
+ return
+ }
+
+ const selectedFiles = readyFiles.slice(0, remainCount)
+ if (selectedFiles.length < readyFiles.length) {
+ proxy.$modal.msgWarning(`鏈�澶氫笂浼� ${props.limit} 涓枃浠讹紝瓒呭嚭閮ㄥ垎宸插拷鐣)
+ }
+
+ selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid))
+ uploadSelectedFiles(selectedFiles)
+ }, 0)
+}
+
+async function uploadSelectedFiles(files) {
+ const validFiles = files.filter((file) => validateFile(file.raw))
+ const invalidFiles = files.filter((file) => !validFiles.includes(file))
+
+ invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+
+ if (!validFiles.length) {
+ uploadRef.value?.clearFiles()
+ return
+ }
+
+ const formData = new FormData()
+ validFiles.forEach((file) => {
+ formData.append(props.uploadFieldName, file.raw)
+ })
+
+ uploading.value = true
+ proxy.$modal.loading('鏂囦欢涓婁紶涓紝璇风◢鍊�...')
+
+ try {
+ const response = await uploadFile(formData)
+ const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
+
+ if (!responseList.length) {
+ proxy.$modal.msgError('涓婁紶鎺ュ彛鏈繑鍥炴暟缁勬暟鎹�')
+ return
+ }
+
+ currentList.value = [...currentList.value, ...responseList]
+ proxy.$modal.msgSuccess('涓婁紶鎴愬姛')
+ } catch (error) {
+ proxy.$modal.msgError(error?.message || '涓婁紶澶辫触')
+ } finally {
+ validFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+ invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+ uploadRef.value?.clearFiles()
+ uploading.value = false
+ proxy.$modal.closeLoading()
+ }
+}
+
+function handleChange(file, uploadFiles) {
+ if (props.disabled || uploading.value) return
+ scheduleUpload(uploadFiles)
+}
+
+function handleRemove(file) {
+ const targetUrl = file.url || getItemUrl(file.rawData)
+ const nextList = currentList.value.filter((item, index) => {
+ const itemUrl = getItemUrl(item)
+ const itemName = getItemName(item, index)
+ return !(itemUrl === targetUrl && itemName === file.name)
+ })
+ currentList.value = nextList
+}
+
+function handleExceed() {
+ proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 涓枃浠禶)
+}
+
+function openFile(file) {
+ const fileUrl = file.url || getItemUrl(file.rawData)
+ if (!fileUrl) return
+ window.open(fileUrl, '_blank')
+}
+
+onBeforeUnmount(() => {
+ clearTimeout(uploadQueueTimer.value)
+})
+</script>
+
+<template>
+ <div class="attachment-upload-file">
+ <el-upload
+ ref="uploadRef"
+ drag
+ :auto-upload="false"
+ :multiple="true"
+ :show-file-list="true"
+ :file-list="displayFileList"
+ :disabled="disabled || uploading"
+ :limit="limit"
+ :on-change="handleChange"
+ :on-remove="handleRemove"
+ :on-exceed="handleExceed"
+ :on-preview="openFile"
+ >
+ <el-icon class="upload-drag-icon"><UploadFilled /></el-icon>
+ <div class="el-upload__text">
+ 灏嗘枃浠舵嫋鍒版澶勶紝鎴� <em>{{ buttonText }}</em>
+ </div>
+ <div class="upload-tip">{{ uploadTip }}</div>
+ </el-upload>
+ </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-upload-file {
+ width: 100%;
+}
+
+.upload-drag-icon {
+ font-size: 40px;
+ color: var(--el-text-color-secondary);
+}
+
+.upload-tip {
+ margin-top: 8px;
+ color: var(--el-text-color-secondary);
+ font-size: 12px;
+}
+</style>
diff --git a/src/components/AttachmentUpload/image/index.vue b/src/components/AttachmentUpload/image/index.vue
new file mode 100644
index 0000000..25bd13c
--- /dev/null
+++ b/src/components/AttachmentUpload/image/index.vue
@@ -0,0 +1,335 @@
+<script setup>
+import {Plus} from '@element-plus/icons-vue'
+import {uploadFile} from '@/api/basicData/common'
+
+const props = defineProps({
+ fileList: {
+ type: Array,
+ default: () => [],
+ },
+ index: {
+ type: Number,
+ default: -1,
+ },
+ childrenKey: {
+ type: String,
+ default: 'images',
+ },
+ limit: {
+ type: Number,
+ default: 10,
+ },
+ fileSize: {
+ type: Number,
+ default: 10,
+ },
+ fileType: {
+ type: Array,
+ default: () => ['png', 'jpg', 'jpeg', 'webp'],
+ },
+ buttonText: {
+ type: String,
+ default: '涓婁紶鍥剧墖',
+ },
+ disabled: {
+ type: Boolean,
+ default: false,
+ },
+ uploadFieldName: {
+ type: String,
+ default: 'files',
+ },
+})
+
+const emit = defineEmits(['update:fileList', 'change'])
+const {proxy} = getCurrentInstance()
+
+const uploadRef = ref()
+const previewVisible = ref(false)
+const previewUrl = ref('')
+const uploadQueueTimer = ref(null)
+const uploading = ref(false)
+const queuedUidSet = ref(new Set())
+
+const currentList = computed({
+ get() {
+ if (props.index > -1) {
+ const row = props.fileList?.[props.index]
+ return Array.isArray(row?.[props.childrenKey]) ? row[props.childrenKey] : []
+ }
+ return Array.isArray(props.fileList) ? props.fileList : []
+ },
+ set(value) {
+ const nextList = Array.isArray(value) ? value : []
+ if (props.index > -1) {
+ const nextModelValue = Array.isArray(props.fileList) ? [...props.fileList] : []
+ const currentRow = nextModelValue[props.index] || {}
+ nextModelValue[props.index] = {
+ ...currentRow,
+ [props.childrenKey]: nextList,
+ }
+ emit('update:fileList', nextModelValue)
+ emit('change', nextList, nextModelValue)
+ return
+ }
+ emit('update:fileList', nextList)
+ emit('change', nextList, nextList)
+ },
+})
+
+const displayFileList = computed(() => {
+ return currentList.value.map((item, index) => ({
+ uid: getItemUid(item, index),
+ name: getItemName(item, index),
+ url: getItemUrl(item),
+ status: 'success',
+ rawData: item,
+ }))
+})
+
+const uploadTip = computed(() => {
+ return `鏀寔 ${props.fileType.join('/')}锛屽崟寮犱笉瓒呰繃 ${props.fileSize}MB`
+})
+
+function getItemUid(item, index) {
+ if (item?.id !== undefined && item?.id !== null) return `${item.id}`
+ return `${getItemName(item, index)}-${getItemUrl(item) || index}`
+}
+
+function getItemUrl(item) {
+ if (!item) return ''
+ if (typeof item === 'string') return item
+ return item.url || item.previewURL || ''
+}
+
+function getItemName(item, index = 0) {
+ if (!item) return `image-${index + 1}`
+ if (typeof item === 'string') return `image-${index + 1}`
+ return item.name || item.fileName || item.originalFilename || `image-${index + 1}`
+}
+
+function normalizeResponseItem(item, index) {
+ if (typeof item === 'string') {
+ return {
+ name: `image-${currentList.value.length + index + 1}`,
+ url: item,
+ }
+ }
+ return Object.assign({}, item, {
+ url: item.url || item.previewURL || item.previewUrl || '',
+ name: item.name || item.originalFilename || item.fileName || `image-${currentList.value.length + index + 1}`,
+ })
+}
+
+function extractResponseArray(response) {
+ if (Array.isArray(response)) return response
+ if (Array.isArray(response?.data)) return response.data
+ if (Array.isArray(response?.data?.data)) return response.data.data
+ if (Array.isArray(response?.payload)) return response.payload
+ if (Array.isArray(response?.payload?.data)) return response.payload.data
+ if (Array.isArray(response?.rows)) return response.rows
+ if (Array.isArray(response?.result)) return response.result
+ return []
+}
+
+function validateFile(rawFile) {
+ let isValidType = false
+ const extension = rawFile.name.includes('.')
+ ? rawFile.name.slice(rawFile.name.lastIndexOf('.') + 1).toLowerCase()
+ : ''
+
+ if (props.fileType.length) {
+ isValidType = props.fileType.some((type) => {
+ const normalizedType = String(type).toLowerCase()
+ return rawFile.type.toLowerCase().includes(normalizedType) || extension === normalizedType
+ })
+ } else {
+ isValidType = rawFile.type.includes('image')
+ }
+
+ if (!isValidType) {
+ proxy.$modal.msgError(`璇蜂笂浼� ${props.fileType.join('/')} 鏍煎紡鐨勫浘鐗嘸)
+ return false
+ }
+
+ const isWithinSize = rawFile.size / 1024 / 1024 <= props.fileSize
+ if (!isWithinSize) {
+ proxy.$modal.msgError(`鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize}MB`)
+ return false
+ }
+
+ return true
+}
+
+function scheduleUpload(uploadFiles) {
+ clearTimeout(uploadQueueTimer.value)
+ uploadQueueTimer.value = setTimeout(() => {
+ const readyFiles = uploadFiles.filter((file) => file.raw && !queuedUidSet.value.has(file.uid))
+ if (!readyFiles.length) return
+
+ const remainCount = props.limit - currentList.value.length
+ if (remainCount <= 0) {
+ proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗嘸)
+ uploadRef.value?.clearFiles()
+ return
+ }
+
+ const selectedFiles = readyFiles.slice(0, remainCount)
+ if (selectedFiles.length < readyFiles.length) {
+ proxy.$modal.msgWarning(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗囷紝瓒呭嚭閮ㄥ垎宸插拷鐣)
+ }
+
+ selectedFiles.forEach((file) => queuedUidSet.value.add(file.uid))
+ uploadSelectedFiles(selectedFiles)
+ }, 0)
+}
+
+async function uploadSelectedFiles(files) {
+ const validFiles = files.filter((file) => validateFile(file.raw))
+ const invalidFiles = files.filter((file) => !validFiles.includes(file))
+
+ invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+
+ if (!validFiles.length) {
+ uploadRef.value?.clearFiles()
+ return
+ }
+
+ const formData = new FormData()
+ validFiles.forEach((file) => {
+ formData.append(props.uploadFieldName, file.raw)
+ })
+
+ uploading.value = true
+ proxy.$modal.loading('鍥剧墖涓婁紶涓紝璇风◢鍊�...')
+
+ try {
+ const response = await uploadFile(formData)
+ const responseList = extractResponseArray(response).map((item, index) => normalizeResponseItem(item, index))
+
+ if (!responseList.length) {
+ proxy.$modal.msgError('涓婁紶鎺ュ彛鏈繑鍥炴暟缁勬暟鎹�')
+ return
+ }
+ console.log('responseList', responseList)
+
+ currentList.value = [...currentList.value, ...responseList]
+ console.log('currentList.value', currentList.value)
+ proxy.$modal.msgSuccess('涓婁紶鎴愬姛')
+ } catch (error) {
+ proxy.$modal.msgError(error?.message || '涓婁紶澶辫触')
+ } finally {
+ validFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+ invalidFiles.forEach((file) => queuedUidSet.value.delete(file.uid))
+ uploadRef.value?.clearFiles()
+ uploading.value = false
+ proxy.$modal.closeLoading()
+ }
+}
+
+function handleChange(file, uploadFiles) {
+ if (props.disabled || uploading.value) return
+ scheduleUpload(uploadFiles)
+}
+
+function handleRemove(file) {
+ const targetUrl = file.url || getItemUrl(file.rawData)
+ const nextList = currentList.value.filter((item, index) => {
+ const itemUrl = getItemUrl(item)
+ const itemName = getItemName(item, index)
+ return !(itemUrl === targetUrl && itemName === file.name)
+ })
+ currentList.value = nextList
+}
+
+function handlePreview(file) {
+ previewUrl.value = file.url || getItemUrl(file.rawData)
+ previewVisible.value = true
+}
+
+function handleExceed() {
+ proxy.$modal.msgError(`鏈�澶氫笂浼� ${props.limit} 寮犲浘鐗嘸)
+}
+
+onBeforeUnmount(() => {
+ clearTimeout(uploadQueueTimer.value)
+})
+</script>
+
+<template>
+ <div class="attachment-upload-image">
+ <el-upload
+ ref="uploadRef"
+ :auto-upload="false"
+ :multiple="true"
+ :show-file-list="true"
+ :file-list="displayFileList"
+ list-type="picture-card"
+ accept="image/*"
+ :disabled="disabled || uploading"
+ :limit="limit"
+ :on-change="handleChange"
+ :on-remove="handleRemove"
+ :on-preview="handlePreview"
+ :on-exceed="handleExceed"
+ >
+ <div class="upload-trigger">
+ <el-icon>
+ <Plus/>
+ </el-icon>
+ <span>{{ buttonText }}</span>
+ </div>
+ </el-upload>
+
+ <div class="upload-tip">
+ {{ uploadTip }}
+ </div>
+
+ <el-dialog v-model="previewVisible" title="鍥剧墖棰勮" width="720px" append-to-body>
+ <img class="preview-image" :src="previewUrl" alt="preview"/>
+ </el-dialog>
+ </div>
+</template>
+
+<style scoped lang="scss">
+.attachment-upload-image {
+ width: 100%;
+}
+
+.upload-trigger {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ color: var(--el-text-color-secondary);
+ font-size: 12px;
+ line-height: 1.2;
+}
+
+.upload-tip {
+ margin-top: 8px;
+ color: var(--el-text-color-secondary);
+ font-size: 12px;
+}
+
+.preview-image {
+ display: block;
+ max-width: 100%;
+ margin: 0 auto;
+}
+
+:deep(.el-upload-list--picture-card) {
+ margin: 0;
+}
+
+:deep(.el-upload--picture-card) {
+ width: 132px;
+ height: 132px;
+}
+
+:deep(.el-upload-list--picture-card .el-upload-list__item) {
+ width: 132px;
+ height: 132px;
+}
+</style>
diff --git a/src/views/productionManagement/productStructure/index.vue b/src/views/productionManagement/productStructure/index.vue
index 8c9cfd1..e05ed3c 100644
--- a/src/views/productionManagement/productStructure/index.vue
+++ b/src/views/productionManagement/productStructure/index.vue
@@ -1,382 +1,514 @@
<template>
<div class="app-container">
- <div class="table_list">
- <div style="text-align: right; margin-bottom: 10px;">
- <el-button type="primary" @click="handleAdd">鏂板</el-button>
- <el-button type="info" plain icon="Upload" @click="handleImport"
- v-hasPermi="['product:bom:import']">瀵煎叆</el-button>
- <el-button type="warning" plain icon="Download" @click="handleExport" :disabled="selectedRows.length !== 1"
- v-hasPermi="['product:bom:export']">瀵煎嚭</el-button>
- <el-button type="danger" plain @click="handleBatchDelete" :disabled="selectedRows.length === 0">鍒犻櫎</el-button>
- </div>
- <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page" :isSelection="true"
- @selection-change="handleSelectionChange" :tableLoading="tableLoading" @pagination="pagination">
- <template #detail="{ row }">
- <el-button type="primary" text @click="showDetail(row)">{{ row.bomNo }}
- </el-button>
- </template>
- </PIMTable>
- </div>
- <StructureEdit v-if="showEdit" v-model:show-model="showEdit" :record="currentRow" />
-
+ <div class="table_list">
+ <div style="text-align: right; margin-bottom: 10px;">
+ <el-button type="primary"
+ @click="handleAdd">鏂板</el-button>
+ <el-button type="info"
+ plain
+ icon="Upload"
+ @click="handleImport"
+ v-hasPermi="['product:bom:import']">瀵煎叆</el-button>
+ <el-button type="warning"
+ plain
+ icon="Download"
+ @click="handleExport"
+ :disabled="selectedRows.length !== 1"
+ v-hasPermi="['product:bom:export']">瀵煎嚭</el-button>
+ <el-button type="danger"
+ plain
+ @click="handleBatchDelete"
+ :disabled="selectedRows.length === 0">鍒犻櫎</el-button>
+ </div>
+ <PIMTable rowKey="id"
+ :column="tableColumn"
+ :tableData="tableData"
+ :page="page"
+ :isSelection="true"
+ @selection-change="handleSelectionChange"
+ :tableLoading="tableLoading"
+ @pagination="pagination">
+ <template #detail="{ row }">
+ <el-button type="primary"
+ text
+ @click="showDetail(row)">{{ row.bomNo }}
+ </el-button>
+ </template>
+ </PIMTable>
+ </div>
+ <StructureEdit v-if="showEdit"
+ v-model:show-model="showEdit"
+ :record="currentRow" />
<!-- 鏂板/缂栬緫寮圭獥 -->
- <el-dialog v-model="dialogVisible" :title="operationType === 'add' ? '鏂板BOM' : '缂栬緫BOM'" width="600px"
- @close="closeDialog">
- <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
- <el-form-item label="浜у搧鍚嶇О" prop="productModelId">
- <el-button type="primary" @click="showProductSelectDialog = true">
+ <el-dialog v-model="dialogVisible"
+ :title="operationType === 'add' ? '鏂板BOM' : '缂栬緫BOM'"
+ width="600px"
+ @close="closeDialog">
+ <el-form ref="formRef"
+ :model="form"
+ :rules="rules"
+ label-width="120px">
+ <el-form-item label="浜у搧鍚嶇О"
+ prop="productModelId">
+ <el-button type="primary"
+ @click="showProductSelectDialog = true">
{{ form.productName || '閫夋嫨浜у搧' }}
</el-button>
</el-form-item>
- <el-form-item label="鐗堟湰鍙�" prop="version">
- <el-input v-model="form.version" placeholder="璇疯緭鍏ョ増鏈彿" clearable />
+ <el-form-item label="鐗堟湰鍙�"
+ prop="version">
+ <el-input v-model="form.version"
+ placeholder="璇疯緭鍏ョ増鏈彿"
+ clearable />
</el-form-item>
- <el-form-item label="澶囨敞" prop="remark">
- <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ュ娉�" clearable />
+ <el-form-item label="澶囨敞"
+ prop="remark">
+ <el-input v-model="form.remark"
+ type="textarea"
+ :rows="3"
+ placeholder="璇疯緭鍏ュ娉�"
+ clearable />
</el-form-item>
</el-form>
<template #footer>
- <el-button type="primary" @click="handleSubmit">纭畾</el-button>
+ <el-button type="primary"
+ @click="handleSubmit">纭畾</el-button>
<el-button @click="closeDialog">鍙栨秷</el-button>
</template>
</el-dialog>
-
<!-- 浜у搧閫夋嫨寮圭獥 -->
- <ProductSelectDialog v-model="showProductSelectDialog" @confirm="handleProductSelect" single />
-
+ <ProductSelectDialog v-model="showProductSelectDialog"
+ @confirm="handleProductSelect"
+ single />
<!-- BOM瀵煎叆瀵硅瘽妗� -->
- <ImportDialog ref="uploadRef" v-model="upload.open" :title="upload.title" :action="upload.url"
- :headers="upload.headers" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress"
- :on-success="handleFileSuccess" :show-download-template="true" @confirm="submitFileForm"
- @download-template="handleDownloadTemplate" @close="handleImportClose" />
+ <ImportDialog ref="uploadRef"
+ v-model="upload.open"
+ :title="upload.title"
+ :action="upload.url"
+ :headers="upload.headers"
+ :disabled="upload.isUploading"
+ :on-progress="handleFileUploadProgress"
+ :on-success="handleFileSuccess"
+ :show-download-template="true"
+ @confirm="submitFileForm"
+ @download-template="handleDownloadTemplate"
+ @close="handleImportClose" />
</div>
</template>
<script setup>
-import { ref, reactive, toRefs, onMounted, getCurrentInstance, defineAsyncComponent } from "vue";
-import { getToken } from "@/utils/auth";
-import { listPage, add, update, batchDelete, exportBom, downloadTemplate } from "@/api/productionManagement/productBom.js";
-import { useRouter } from 'vue-router'
-import { ElMessageBox } from 'element-plus'
-import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
-import ImportDialog from "@/components/Dialog/ImportDialog.vue";
+ import {
+ ref,
+ reactive,
+ toRefs,
+ onMounted,
+ getCurrentInstance,
+ defineAsyncComponent,
+ } from "vue";
+ import { getToken } from "@/utils/auth";
+ import {
+ listPage,
+ add,
+ copy,
+ update,
+ batchDelete,
+ exportBom,
+ downloadTemplate,
+ } from "@/api/productionManagement/productBom.js";
+ import { useRouter } from "vue-router";
+ import { ElMessageBox } from "element-plus";
+ import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
+ import ImportDialog from "@/components/Dialog/ImportDialog.vue";
-const router = useRouter()
-const { proxy } = getCurrentInstance()
-const StructureEdit = defineAsyncComponent(() => import('@/views/productionManagement/productStructure/StructureEdit.vue'))
+ const router = useRouter();
+ const { proxy } = getCurrentInstance();
+ const StructureEdit = defineAsyncComponent(() =>
+ import("@/views/productionManagement/productStructure/StructureEdit.vue")
+ );
-const tableColumn = ref([
- {
- label: "BOM缂栧彿",
- prop: "bomNo",
- dataType: 'slot',
- slot: "detail",
- minWidth: 140
- },
- {
- label: "浜у搧鍚嶇О",
- prop: "productName",
+ const tableColumn = ref([
+ {
+ label: "BOM缂栧彿",
+ prop: "bomNo",
+ dataType: "slot",
+ slot: "detail",
+ minWidth: 140,
+ },
+ {
+ label: "浜у搧鍚嶇О",
+ prop: "productName",
- minWidth: 160
- },
- {
- label: "瑙勬牸鍨嬪彿",
- prop: "productModelName",
- minWidth: 140
- },
- {
- label: "鐗堟湰鍙�",
- prop: "version",
- width: 100
- },
- {
- label: "澶囨敞",
- prop: "remark",
- minWidth: 160
- },
- {
- dataType: "action",
- label: "鎿嶄綔",
- align: "center",
- fixed: "right",
- width: 150,
- operation: [
- {
- name: "缂栬緫",
- type: "text",
- clickFun: (row) => {
- handleEdit(row)
- }
- },
- {
- name: "鍒犻櫎",
- type: "danger",
- link: true,
- clickFun: (row) => {
- handleDelete(row)
- }
- }
- ]
- }
-]);
+ minWidth: 160,
+ },
+ {
+ label: "瑙勬牸鍨嬪彿",
+ prop: "productModelName",
+ minWidth: 140,
+ },
+ {
+ label: "鐗堟湰鍙�",
+ prop: "version",
+ width: 100,
+ },
+ {
+ label: "澶囨敞",
+ prop: "remark",
+ minWidth: 160,
+ },
+ {
+ dataType: "action",
+ label: "鎿嶄綔",
+ align: "center",
+ fixed: "right",
+ width: 250,
+ operation: [
+ {
+ name: "澶嶅埗",
+ type: "text",
+ clickFun: row => {
+ handleCopy(row);
+ },
+ },
+ {
+ name: "缂栬緫",
+ type: "text",
+ clickFun: row => {
+ handleEdit(row);
+ },
+ },
+ {
+ name: "鍒犻櫎",
+ type: "danger",
+ link: true,
+ clickFun: row => {
+ handleDelete(row);
+ },
+ },
+ ],
+ },
+ ]);
-const tableData = ref([]);
-const tableLoading = ref(false);
-const showEdit = ref(false);
-const selectedRows = ref([]);
-const currentRow = ref({});
-const dialogVisible = ref(false);
-const operationType = ref('add'); // add | edit
-const formRef = ref(null);
-const showProductSelectDialog = ref(false);
+ const tableData = ref([]);
+ const tableLoading = ref(false);
+ const showEdit = ref(false);
+ const selectedRows = ref([]);
+ const currentRow = ref({});
+ const dialogVisible = ref(false);
+ const operationType = ref("add"); // add | edit
+ const formRef = ref(null);
+ const showProductSelectDialog = ref(false);
-// BOM瀵煎叆鍙傛暟
-const upload = reactive({
- // 鏄惁鏄剧ず寮瑰嚭灞傦紙BOM瀵煎叆锛�
- open: false,
- // 寮瑰嚭灞傛爣棰橈紙BOM瀵煎叆锛�
- title: "",
- // 鏄惁绂佺敤涓婁紶
- isUploading: false,
- // 璁剧疆涓婁紶鐨勮姹傚ご閮�
- headers: { Authorization: "Bearer " + getToken() },
- // 涓婁紶鐨勫湴鍧�
- url: import.meta.env.VITE_APP_BASE_API + "/technologyBom/uploadBom"
-});
-
-const page = reactive({
- current: 1,
- size: 10,
- total: 0,
-});
-
-const data = reactive({
- form: {
- id: undefined,
- productName: "",
- productModelName: "",
- productModelId: "",
- remark: "",
- version: ""
- },
- rules: {
- productModelId: [{ required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" }],
- version: [{ required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur" }]
- }
-});
-
-const { form, rules } = toRefs(data);
-
-// 琛ㄦ牸閫夋嫨鏁版嵁
-const handleSelectionChange = (selection) => {
- selectedRows.value = selection;
-};
-
-// 鍒嗛〉
-const pagination = (obj) => {
- page.current = obj.page;
- page.size = obj.limit;
- getList();
-};
-
-// 鏌ヨ鍒楄〃
-const getList = () => {
- tableLoading.value = true;
- listPage({
- current: page.current,
- size: page.size,
- })
- .then((res) => {
- const records = res?.data?.records || [];
- tableData.value = records;
- page.total = res?.data?.total || 0;
- })
- .catch((err) => {
- console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
- })
- .finally(() => {
- tableLoading.value = false;
- });
-};
-
-// 鏂板
-const handleAdd = () => {
- operationType.value = 'add';
- Object.assign(form.value, {
- id: undefined,
- productName: "",
- productModelName: "",
- productModelId: "",
- remark: "",
- version: ""
+ // BOM瀵煎叆鍙傛暟
+ const upload = reactive({
+ // 鏄惁鏄剧ず寮瑰嚭灞傦紙BOM瀵煎叆锛�
+ open: false,
+ // 寮瑰嚭灞傛爣棰橈紙BOM瀵煎叆锛�
+ title: "",
+ // 鏄惁绂佺敤涓婁紶
+ isUploading: false,
+ // 璁剧疆涓婁紶鐨勮姹傚ご閮�
+ headers: { Authorization: "Bearer " + getToken() },
+ // 涓婁紶鐨勫湴鍧�
+ url: import.meta.env.VITE_APP_BASE_API + "/technologyBom/uploadBom",
});
- dialogVisible.value = true;
-};
-// 缂栬緫
-const handleEdit = (row) => {
- operationType.value = 'edit';
- Object.assign(form.value, {
- id: row.id,
- productName: row.productName || "",
- productModelName: row.productModelName || "",
- productModelId: row.productModelId || "",
- remark: row.remark || "",
- version: row.version || ""
+ const page = reactive({
+ current: 1,
+ size: 10,
+ total: 0,
});
- dialogVisible.value = true;
-};
-// 鍒犻櫎锛堝崟鏉★級
-const handleDelete = (row) => {
- ElMessageBox.confirm('纭鍒犻櫎璇OM锛�', '鎻愮ず', {
- confirmButtonText: '纭',
- cancelButtonText: '鍙栨秷',
- type: 'warning'
- })
- .then(() => {
- batchDelete([row.id])
- .then(() => {
- proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛');
- getList();
- })
- .catch(() => {
- proxy.$modal.msgError('鍒犻櫎澶辫触');
- });
- })
- .catch(() => { });
-};
-
-// 鎵归噺鍒犻櫎
-const handleBatchDelete = () => {
- if (!selectedRows.value.length) {
- proxy.$modal.msgWarning('璇烽�夋嫨鏁版嵁');
- return;
- }
- const ids = selectedRows.value.map(item => item.id);
- ElMessageBox.confirm('閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�', '鍒犻櫎鎻愮ず', {
- confirmButtonText: '纭',
- cancelButtonText: '鍙栨秷',
- type: 'warning'
- })
- .then(() => {
- batchDelete(ids)
- .then(() => {
- proxy.$modal.msgSuccess('鍒犻櫎鎴愬姛');
- getList();
- })
- .catch(() => {
- proxy.$modal.msgError('鍒犻櫎澶辫触');
- });
- })
- .catch(() => { });
-};
-
-// 浜у搧閫夋嫨
-const handleProductSelect = (products) => {
- if (products && products.length > 0) {
- const product = products[0];
- form.value.productModelId = product.id;
- form.value.productName = product.productName;
- form.value.productModelName = product.model;
- }
- showProductSelectDialog.value = false;
-};
-
-// 鎻愪氦琛ㄥ崟
-const handleSubmit = () => {
- formRef.value.validate((valid) => {
- if (valid) {
- const payload = { ...form.value };
- if (operationType.value === 'add') {
- add(payload)
- .then(() => {
- proxy.$modal.msgSuccess('鏂板鎴愬姛');
- closeDialog();
- getList();
- })
- .catch(() => {
- proxy.$modal.msgError('鏂板澶辫触');
- });
- } else {
- update(payload)
- .then(() => {
- proxy.$modal.msgSuccess('淇敼鎴愬姛');
- closeDialog();
- getList();
- })
- .catch(() => {
- proxy.$modal.msgError('淇敼澶辫触');
- });
- }
- }
+ const data = reactive({
+ form: {
+ id: undefined,
+ productName: "",
+ productModelName: "",
+ productModelId: "",
+ remark: "",
+ version: "",
+ },
+ rules: {
+ productModelId: [
+ { required: true, message: "璇烽�夋嫨浜у搧", trigger: "change" },
+ ],
+ version: [{ required: true, message: "璇疯緭鍏ョ増鏈彿", trigger: "blur" }],
+ },
});
-};
-// 鍏抽棴寮圭獥
-const closeDialog = () => {
- dialogVisible.value = false;
- formRef.value?.resetFields();
-};
+ const { form, rules } = toRefs(data);
-// 瀵煎叆鎸夐挳鎿嶄綔
-const handleImport = () => {
- upload.title = "BOM瀵煎叆";
- upload.open = true;
-};
+ // 琛ㄦ牸閫夋嫨鏁版嵁
+ const handleSelectionChange = selection => {
+ selectedRows.value = selection;
+ };
-// 鍏抽棴瀵煎叆瀵硅瘽妗嗘椂娓呴櫎鏂囦欢
-const handleImportClose = () => {
- proxy.$refs["uploadRef"].clearFiles();
-};
-
-// 鏂囦欢涓婁紶涓鐞�
-const handleFileUploadProgress = (event, file, fileList) => {
- upload.isUploading = true;
-};
-
-// 鏂囦欢涓婁紶鎴愬姛澶勭悊
-const handleFileSuccess = (response, file, fileList) => {
- upload.open = false;
- upload.isUploading = false;
- proxy.$refs["uploadRef"].clearFiles();
- if (response.code === 200) {
- proxy.$modal.msgSuccess(response.msg || "瀵煎叆鎴愬姛");
+ // 鍒嗛〉
+ const pagination = obj => {
+ page.current = obj.page;
+ page.size = obj.limit;
getList();
- } else {
- proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
- }
-};
+ };
-// 鎻愪氦涓婁紶鏂囦欢
-const submitFileForm = () => {
- proxy.$refs["uploadRef"].submit();
-};
+ // 鏌ヨ鍒楄〃
+ const getList = () => {
+ tableLoading.value = true;
+ listPage({
+ current: page.current,
+ size: page.size,
+ })
+ .then(res => {
+ const records = res?.data?.records || [];
+ tableData.value = records;
+ page.total = res?.data?.total || 0;
+ })
+ .catch(err => {
+ console.error("鑾峰彇鍒楄〃澶辫触锛�", err);
+ })
+ .finally(() => {
+ tableLoading.value = false;
+ });
+ };
-// 瀵煎嚭鎸夐挳鎿嶄綔
-const handleExport = () => {
- if (selectedRows.value.length !== 1) {
- proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉℃暟鎹繘琛屽鍑�");
- return;
- }
+ // 鏂板
+ const handleAdd = () => {
+ operationType.value = "add";
+ Object.assign(form.value, {
+ id: undefined,
+ productName: "",
+ productModelName: "",
+ productModelId: "",
+ remark: "",
+ version: "",
+ });
+ dialogVisible.value = true;
+ };
+ const handleCopy = row => {
+ // handleAdd(row);
+ ElMessageBox.confirm("纭澶嶅埗璇OM锛�", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ copy({
+ id: row.id,
+ })
+ .then(() => {
+ proxy.$modal.msgSuccess("澶嶅埗鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("澶嶅埗澶辫触");
+ });
+ })
+ .catch(() => {});
+ };
- const bomId = selectedRows.value[0].id;
- const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
+ // 缂栬緫
+ const handleEdit = row => {
+ operationType.value = "edit";
+ Object.assign(form.value, {
+ id: row.id,
+ productName: row.productName || "",
+ productModelName: row.productModelName || "",
+ productModelId: row.productModelId || "",
+ remark: row.remark || "",
+ version: row.version || "",
+ });
+ dialogVisible.value = true;
+ };
- exportBom(bomId).then(res => {
- // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
- if (!res) {
- proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+ // 鍒犻櫎锛堝崟鏉★級
+ const handleDelete = row => {
+ ElMessageBox.confirm("纭鍒犻櫎璇OM锛�", "鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ batchDelete([row.id])
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鍒犻櫎澶辫触");
+ });
+ })
+ .catch(() => {});
+ };
+
+ // 鎵归噺鍒犻櫎
+ const handleBatchDelete = () => {
+ if (!selectedRows.value.length) {
+ proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+ return;
+ }
+ const ids = selectedRows.value.map(item => item.id);
+ ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎鎻愮ず", {
+ confirmButtonText: "纭",
+ cancelButtonText: "鍙栨秷",
+ type: "warning",
+ })
+ .then(() => {
+ batchDelete(ids)
+ .then(() => {
+ proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鍒犻櫎澶辫触");
+ });
+ })
+ .catch(() => {});
+ };
+
+ // 浜у搧閫夋嫨
+ const handleProductSelect = products => {
+ if (products && products.length > 0) {
+ const product = products[0];
+ form.value.productModelId = product.id;
+ form.value.productName = product.productName;
+ form.value.productModelName = product.model;
+ }
+ showProductSelectDialog.value = false;
+ };
+
+ // 鎻愪氦琛ㄥ崟
+ const handleSubmit = () => {
+ formRef.value.validate(valid => {
+ if (valid) {
+ const payload = { ...form.value };
+ if (operationType.value === "add") {
+ add(payload)
+ .then(() => {
+ proxy.$modal.msgSuccess("鏂板鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("鏂板澶辫触");
+ });
+ } else {
+ update(payload)
+ .then(() => {
+ proxy.$modal.msgSuccess("淇敼鎴愬姛");
+ closeDialog();
+ getList();
+ })
+ .catch(() => {
+ proxy.$modal.msgError("淇敼澶辫触");
+ });
+ }
+ }
+ });
+ };
+
+ // 鍏抽棴寮圭獥
+ const closeDialog = () => {
+ dialogVisible.value = false;
+ formRef.value?.resetFields();
+ };
+
+ // 瀵煎叆鎸夐挳鎿嶄綔
+ const handleImport = () => {
+ upload.title = "BOM瀵煎叆";
+ upload.open = true;
+ };
+
+ // 鍏抽棴瀵煎叆瀵硅瘽妗嗘椂娓呴櫎鏂囦欢
+ const handleImportClose = () => {
+ proxy.$refs["uploadRef"].clearFiles();
+ };
+
+ // 鏂囦欢涓婁紶涓鐞�
+ const handleFileUploadProgress = (event, file, fileList) => {
+ upload.isUploading = true;
+ };
+
+ // 鏂囦欢涓婁紶鎴愬姛澶勭悊
+ const handleFileSuccess = (response, file, fileList) => {
+ upload.open = false;
+ upload.isUploading = false;
+ proxy.$refs["uploadRef"].clearFiles();
+ if (response.code === 200) {
+ proxy.$modal.msgSuccess(response.msg || "瀵煎叆鎴愬姛");
+ getList();
+ } else {
+ proxy.$alert(
+ "<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" +
+ response.msg +
+ "</div>",
+ "瀵煎叆缁撴灉",
+ { dangerouslyUseHTMLString: true }
+ );
+ }
+ };
+
+ // 鎻愪氦涓婁紶鏂囦欢
+ const submitFileForm = () => {
+ proxy.$refs["uploadRef"].submit();
+ };
+
+ // 瀵煎嚭鎸夐挳鎿嶄綔
+ const handleExport = () => {
+ if (selectedRows.value.length !== 1) {
+ proxy.$modal.msgWarning("璇烽�夋嫨涓�鏉℃暟鎹繘琛屽鍑�");
return;
}
- const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
- const downloadElement = document.createElement('a');
+ const bomId = selectedRows.value[0].id;
+ const fileName = `BOM_${selectedRows.value[0].bomNo || bomId}.xlsx`;
+
+ exportBom(bomId)
+ .then(res => {
+ // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+ if (!res) {
+ proxy.$modal.msgError("瀵煎嚭澶辫触锛岃繑鍥炴暟鎹负绌�");
+ return;
+ }
+
+ const blob = new Blob([res], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const downloadElement = document.createElement("a");
+ const href = window.URL.createObjectURL(blob);
+
+ downloadElement.style.display = "none";
+ downloadElement.href = href;
+ downloadElement.download = fileName;
+
+ document.body.appendChild(downloadElement);
+ downloadElement.click();
+
+ document.body.removeChild(downloadElement);
+ window.URL.revokeObjectURL(href);
+
+ proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
+ })
+ .catch(err => {
+ console.error("瀵煎嚭寮傚父锛�", err);
+ proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+ });
+ };
+
+ // 涓嬭浇妯℃澘
+ const handleDownloadTemplate = async () => {
+ const res = await downloadTemplate();
+ // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
+ if (!res) {
+ proxy.$modal.msgError("涓嬭浇澶辫触锛岃繑鍥炴暟鎹负绌�");
+ return;
+ }
+
+ const blob = new Blob([res], {
+ type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ });
+ const downloadElement = document.createElement("a");
const href = window.URL.createObjectURL(blob);
- downloadElement.style.display = 'none';
downloadElement.href = href;
- downloadElement.download = fileName;
+ downloadElement.download = "BOM妯℃澘.xlsx";
document.body.appendChild(downloadElement);
downloadElement.click();
@@ -384,52 +516,23 @@
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href);
- proxy.$modal.msgSuccess("瀵煎嚭鎴愬姛");
- }).catch(err => {
- console.error("瀵煎嚭寮傚父锛�", err);
- proxy.$modal.msgError("绯荤粺寮傚父锛屽鍑哄け璐�");
+ proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
+ };
+
+ // 鏌ョ湅璇︽儏
+ const showDetail = row => {
+ router.push({
+ path: "/productionManagement/productStructureDetail",
+ query: {
+ id: row.id,
+ bomNo: row.bomNo || "",
+ productName: row.productName || "",
+ productModelName: row.productModelName || "",
+ },
+ });
+ };
+
+ onMounted(() => {
+ getList();
});
-};
-
-// 涓嬭浇妯℃澘
-const handleDownloadTemplate = async () => {
- const res = await downloadTemplate();
- // 杩斿洖鐨勬暟鎹槸鍚︿负绌�
- if (!res) {
- proxy.$modal.msgError("涓嬭浇澶辫触锛岃繑鍥炴暟鎹负绌�");
- return;
- }
-
- const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
- const downloadElement = document.createElement('a');
- const href = window.URL.createObjectURL(blob);
-
- downloadElement.href = href;
- downloadElement.download = "BOM妯℃澘.xlsx";
-
- document.body.appendChild(downloadElement);
- downloadElement.click();
-
- document.body.removeChild(downloadElement);
- window.URL.revokeObjectURL(href);
-
- proxy.$modal.msgSuccess("涓嬭浇鎴愬姛");
-};
-
-// 鏌ョ湅璇︽儏
-const showDetail = (row) => {
- router.push({
- path: '/productionManagement/productStructureDetail',
- query: {
- id: row.id,
- bomNo: row.bomNo || '',
- productName: row.productName || '',
- productModelName: row.productModelName || ''
- }
- });
-};
-
-onMounted(() => {
- getList();
-});
</script>
--
Gitblit v1.9.3