From 7c86b549b27bd54f6bd5de81c13f8242f91c87ff Mon Sep 17 00:00:00 2001 From: 张诺 <zhang_12370@163.com> Date: 星期一, 16 六月 2025 18:03:27 +0800 Subject: [PATCH] 优化文件上传组件及表格显示 --- src/views/production/components/ProductionDialog.vue | 613 ++++++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 468 insertions(+), 145 deletions(-) diff --git a/src/views/production/components/ProductionDialog.vue b/src/views/production/components/ProductionDialog.vue index dedb7b3..d95d9ae 100644 --- a/src/views/production/components/ProductionDialog.vue +++ b/src/views/production/components/ProductionDialog.vue @@ -1,107 +1,223 @@ <template> - <el-dialog v-model="dialogVisible" :title="dialogType === 'add' ? '鏂板鐢熶骇鍔犲伐' : '缂栬緫鐢熶骇鍔犲伐'" width="1200px" - :close-on-click-modal="false" @close="handleClose"> - <el-form ref="formRef" :model="formData" :rules="rules" class="production-form"> - <el-row :gutter="24"> - <el-col :span="6"> - <el-form-item label="鐓ょ" prop="category"> - <el-select v-model="formData.category" placeholder="璇烽�夋嫨鐓ょ" clearable style="width: 100%" @change="selectChange"> - <el-option label="鐐肩劍" value="鐐肩劍" /> - <el-option label="姘旂叅" value="姘旂叅" /> - <el-option label="鏃犵儫鐓�" value="鏃犵儫鐓�" /> - <el-option label="闀跨劙鐓�" value="闀跨劙鐓�" /> - <el-option label="璐叅" value="璐叅" /> - </el-select> - </el-form-item> + <el-dialog + v-model="dialogVisible" + :title="dialogType === 'add' ? '鏂板鐢熶骇鍔犲伐' : '缂栬緫鐢熶骇鍔犲伐'" + width="1200px" + :close-on-click-modal="false" + @close="handleClose" + > + <el-row :gutter="10" style="margin-bottom: 10px"> + <el-col :span="3"> + <el-button type="primary" @click="handlData" + ><el-icon> + <Plus /> </el-icon + >閫夋嫨鏁版嵁</el-button + > + </el-col> + <el-col :span="4"> + <el-button + type="danger" + @click="removeSelectedData" + :disabled="tableData.length === 0" + > + <el-icon> + <Delete /> + </el-icon> + 娓呯┖宸查�� + </el-button> + </el-col> + <el-col :span="17" style="text-align: right; line-height: 32px"> + <el-text type="info" size="small"> + 宸查�夋嫨 {{ tableData.length }} 椤规暟鎹� + <span v-if="tableData.length > 0"> + 锛屾�讳娇鐢ㄩ噺: {{ totalUsedQuantity.toFixed(2) }} + </span> + </el-text> + </el-col> + </el-row> + <ETableModify + :columns="columns" + :showOperations="false" + height="200" + @cell-edit="handleCellEdit" + :tableData="tableData" + :showOverflowTooltip="false" + @row-click="handleRowClick" + :editableColumns="['usedQuantity']" + @delete="handleRemoveItem" + /> + <div class="empty-table"> + <h1>鐢熶骇鏄庣粏</h1> + <el-row :gutter="10"> + <el-col :span="2"> + <el-button type="primary" @click="addNewRow"> + <el-icon> + <Plus /> + </el-icon> + 鏂板 + </el-button> </el-col> - <el-col :span="6"> - <el-form-item label="鐑��" prop="Calorific"> - <el-input v-model="formData.Calorific" placeholder="璇疯緭鍏ョ儹鍊�" clearable /> - </el-form-item> + <el-col :span="2"> + <el-button type="danger" @click="clearAllRows"> + <el-icon> + <Delete /> + </el-icon> + 娓呯┖ + </el-button> </el-col> - <el-col :span="4" :offset="8"> - <el-button type="primary" @click="search">鏌ヨ</el-button> - <el-button @click="reset">閲嶇疆</el-button> - </el-col> + <!-- <el-col :span="2"> + <el-button type="warning" @click="calculateAllCosts"> + <el-icon> + <Warning /> + </el-icon> 閲嶆柊璁$畻 + </el-button> + </el-col> --> </el-row> - </el-form> - <ETable :columns="columns" height="200" @cell-edit="handleCellEdit" :showOperations="false" :tableData="tableData" @row-click="handleRowClick" :editableColumns="['used']" /> - <el-row :gutter="10"> - <el-col :span="4"> - <h1>鐢熶骇鏄庣粏</h1> - </el-col> - </el-row> - <el-row :gutter="10"> - <el-col :span="2"> - <el-button type="primary"> - <el-icon><Plus /></el-icon> 鏂板 - </el-button> - </el-col> - <el-col :span="2"><el-button type="danger"> - <el-icon><Delete /></el-icon> 鍒犻櫎 - </el-button></el-col> - <el-col :span="2"> - <el-button type="warning"> - <el-icon><Warning /></el-icon> 淇敼 - </el-button> - </el-col> - </el-row> + <ProductionDetailsTable + v-model="detailsTableData" + :border="false" + :show-operations="true" + :auto-calculate="true" + @input-change="handleDetailsChange" + @delete-row="handleDeleteRow" + /> + </div> + <template #footer> <div class="dialog-footer"> - <el-button @click="handleClose">鍙� 娑�</el-button> - <el-button type="primary" :loading="loading" @click="handleSubmit">纭� 瀹�</el-button> + <el-button @click="handleClose" v-if="dialogType === 'add'" + >鍙� 娑�</el-button + > + <el-button @click="handleReset" v-if="dialogType === 'edit'" + >閲� 缃�</el-button + > + <el-button type="primary" :loading="loading" @click="handleSubmit" + >纭� 瀹�</el-button + > </div> - </template> + </el-dialog> + <el-dialog + v-model="innerVisible" + width="1000" + title="閫夋嫨閰嶇疆鏁版嵁" + center + append-to-body + > + <div style="margin-bottom: 10px"> + <el-alert + v-if="tableData.length > 0" + :title="`褰撳墠宸查�夋嫨 ${tableData.length} 鏉℃暟鎹甡" + type="info" + :closable="false" + show-icon + /> + </div> + <ETable + @selection-change="handleSelectionChange" + :showOperations="false" + ref="etableRef" + :columns="formalDatabaseColumns" + :tableData="formalDatabaseData" + :defaultSelectedIds="selectedIds" + :rowKey="'id'" + height="400" + @cell-edit="handleCellEdit" + :show-selection="true" + /> + <el-row :gutter="24" style="margin-top: 15px"> + <el-col :span="12"> + <el-text type="info"> + 宸查�夋嫨 {{ formalDatabaseSelectedData.length }} 鏉℃暟鎹� + </el-text> + </el-col> + <el-col :span="12" style="text-align: right"> + <el-button @click="innerVisible = false">鍙栨秷</el-button> + <el-button + type="primary" + @click="handleSelectData" + :disabled="formalDatabaseSelectedData.length === 0" + > + 纭畾娣诲姞 + </el-button> + </el-col> + </el-row> </el-dialog> </template> <script setup> -import { ref, reactive, watch } from 'vue' -import ETable from '@/components/Table/ETable.vue' -import { ElMessage } from 'element-plus' -import { Delete, Warning } from '@element-plus/icons-vue' +import { ref, reactive, watch, onMounted, nextTick, computed } from "vue"; +import ETable from "@/components/Table/ETable.vue"; +import ETableModify from "@/components/Table/EtableModify.vue"; +import ProductionDetailsTable from "./ProductionDetailsTable.vue"; +import { ElMessage, ElMessageBox, ElAlert, ElText } from "element-plus"; +import { Delete, Warning, Plus } from "@element-plus/icons-vue"; +import { getOfficialAll, addOrEditPM } from "@/api/production/index.js"; +import { getCurrentInstance } from "vue"; const props = defineProps({ visible: { type: Boolean, - default: false + default: false, }, type: { type: String, - default: 'add' // 'add' 鎴� 'edit' + default: "add", // 'add' 鎴� 'edit' }, rowData: { type: Object, - default: () => ({}) - } -}) -const dialogVisible = defineModel('visible', { + default: () => ({}), + }, +}); +const dialogVisible = defineModel("visible", { type: Boolean, - default: false -}) -const emit = defineEmits(['update:visible', 'success']) + default: false, +}); +const emit = defineEmits(["update:visible", "success"]); -const dialogType = ref('add') -const loading = ref(false) -const formRef = ref(null) - -const tableData = ref([]) -const currentRow = ref(null) +const innerVisible = ref(false); +const dialogType = ref("add"); +const loading = ref(false); +const formRef = ref(null); +const tableData = ref([]); +const currentRow = ref(null); const columns = [ - { label: '鐓ょ', prop: 'category' }, - { label: '鐑��', prop: 'Calorific' }, - { label: '搴撳瓨鏁伴噺', prop: 'stock' }, - { label: '鏈浣跨敤鏁伴噺', prop: 'used' }, -] + { label: "鐓ょ", prop: "coal", minwidth: 120 }, + { label: "搴撳瓨鏁伴噺", prop: "inventoryQuantity", minwidth: 100 }, + { + label: "浣跨敤鏁伴噺", + prop: "usedQuantity", + editable: true, + minwidth: 120, + editType: "number", + }, +]; +const etableRef = ref(null); +const selectedIds = ref([]); // 榛樿閫変腑鐨処D鏁扮粍 +// 璋冭瘯鍑芥暟锛氶獙璇両D鍖归厤閫昏緫 +const debugIdMatching = () => { + if (formalDatabaseData.value.length > 0 && selectedIds.value.length > 0) { + const matchedRows = formalDatabaseData.value.filter((row) => + selectedIds.value.includes(row.id) + ); + } +}; +const detailsTableData = ref([]); const handleRowClick = (row) => { - currentRow.value = row - console.log('褰撳墠琛屾暟鎹�:', currentRow.value) -} + currentRow.value = row; +}; +const formalDatabaseColumns = ref([ + { prop: "supplierName", label: "渚涘簲鍟嗗悕绉�", minwidth: 150 }, + { prop: "coal", label: "鐓ょ绫诲瀷", minwidth: 60 }, + { prop: "inventoryQuantity", label: "搴撳瓨鏁伴噺", minwidth: 80 }, + { prop: "unit", label: "鍗曚綅", minwidth: 100 }, + { prop: "priceExcludingTax", label: "鍗曚环锛堜笉鍚◣锛�", minwidth: 80 }, + { prop: "createTime", label: "鐧昏鏃ユ湡", minwidth: 400 }, +]); // 琛ㄥ崟鏁版嵁 const formData = reactive({ - category: '', - unit: '', + category: "", + unit: "", productionVolume: 0, laborCost: 0, materialCost: 0, @@ -109,68 +225,178 @@ totalCost: 0, totalPrice: 0, profit: 0, - reviewer: '', - date: '' -}) - -// 琛ㄥ崟楠岃瘉瑙勫垯 -const rules = { - category: [{ required: true, message: '璇烽�夋嫨鐓ょ', trigger: 'change' }], -} - -const search = () => { - // 鏌ヨ閫昏緫 - if (!formData.category) { - return this.$message.error('璇烽�夋嫨鐓ょ') + reviewer: "", + date: "", +}); +const handlData = async () => { + innerVisible.value = true; + let res = await getOfficialAll(); + if (res.code === 200) { + formalDatabaseData.value = res.data; + const existingOfficialIds = tableData.value + .map((item) => item.officialId) + .filter((id) => id); + selectedIds.value = existingOfficialIds; + debugIdMatching(); + nextTick(() => { + setTimeout(() => { + if (etableRef.value && existingOfficialIds.length > 0) { + etableRef.value.setDefaultSelection(); + } + }, 100); + }); + } else { + ElMessage.error("鑾峰彇閰嶇疆鏁版嵁澶辫触"); } - loading.value = true - // 妯℃嫙鏌ヨ鏁版嵁 - setTimeout(() => { - // 鍋囨暟鎹� - tableData.value = [ - { category: '鐐肩劍', Calorific: '6000', stock: 100, used: 20 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '姘旂叅', Calorific: '5500', stock: 80, used: 15 }, - { category: '鏃犵儫鐓�', Calorific: '7000', stock: 120, used: 30 } - ] - loading.value = false - }, 1000) -} +}; +// 鎵嬪姩璁剧疆琛ㄦ牸閫変腑鐘舵�� +const setTableSelection = (ids) => { + if (!etableRef.value || !Array.isArray(ids) || ids.length === 0) { + return; + } -const reset = () => { - // formRef - formRef.value?.resetFields() -} + nextTick(() => { + setTimeout(() => { + try { + // 鍏堟竻闄ゆ墍鏈夐�変腑 + etableRef.value.clearSelection(); -const selectChange = (value) => { -} + // 鎵惧埌闇�瑕侀�変腑鐨勮骞惰缃�変腑鐘舵�� + // 娉ㄦ剰锛歩ds涓槸officialId锛岄渶瑕佸尮閰峟ormalDatabaseData涓殑id瀛楁 + const rowsToSelect = formalDatabaseData.value.filter((row) => + ids.includes(row.id) + ); + if (rowsToSelect.length > 0) { + etableRef.value.setRowsSelection(rowsToSelect, true); + console.log("閫変腑鐘舵�佽缃畬鎴�"); + } else { + } + } catch (error) { + console.error("璁剧疆閫変腑鐘舵�佸け璐�:", error); + } + }, 150); + }); +}; +const formalDatabaseData = ref([]); +const formalDatabaseSelectedData = ref([]); +formalDatabaseData.value = []; +// 鍒濆鍖� +const Initialization = () => { + tableData.value = []; + detailsTableData.value = []; + copyForm.value = null; + dialogType.value = "add"; +}; +const copyForm = ref(null); +const editInitialization = (data) => { + copyForm.value = { ...data }; // 娣辨嫹璐濇暟鎹� + tableData.value = data.productionInventoryList || []; + detailsTableData.value = data.productionList || []; + dialogType.value = "edit"; + + // 璁剧疆榛樿閫変腑鐨処D锛屼娇鐢╫fficialId鏉ュ尮閰� + const existingOfficialIds = tableData.value + .map((item) => item.officialId) + .filter((id) => id); + selectedIds.value = existingOfficialIds; +}; +// 鐩戝惉瀵硅瘽妗嗙姸鎬侊紝鍦ㄦ墦寮�鏃惰缃�変腑鐘舵�� +watch(innerVisible, (newVal) => { + if (newVal && selectedIds.value.length > 0) { + console.log("瀵硅瘽妗嗘墦寮�锛岃缃�変腑鐘舵��"); + setTimeout(() => setTableSelection(selectedIds.value), 200); + } + // 瀵硅瘽妗嗗叧闂椂娓呯┖閫夋嫨鐘舵�� + if (!newVal) { + formalDatabaseSelectedData.value = []; + } +}); + +defineExpose({ + Initialization, + editInitialization, +}); +const handleSelectData = (row) => { + tableData.value = []; + if (!innerVisible.value) return; + const selectedData = formalDatabaseSelectedData.value; + if (selectedData.length === 0) { + ElMessage.warning("璇疯嚦灏戦�夋嫨涓�鏉℃暟鎹�"); + return; + } + let addedCount = 0; + let duplicateCount = 0; + selectedData.forEach((item) => { + const newItem = { + ...item, // 澶嶅埗鎵�鏈夊師濮嬫暟鎹� + officialId: item.id, // 淇濆瓨鍘熷鐨刬d浣滀负officialId + usedQuantity: 0, // 鍒濆浣跨敤鏁伴噺涓�0 + // 鍙互鏍规嵁闇�瑕佹坊鍔犲叾浠栧瓧娈� + }; + tableData.value.push(newItem); + addedCount++; + }); + + // 鏇存柊selectedIds锛岀‘淇濆寘鍚墍鏈夊綋鍓峵ableData涓殑officialId + const allOfficialIds = tableData.value + .map((item) => item.officialId) + .filter((id) => id); + selectedIds.value = allOfficialIds; + + console.log("鏇存柊鍚庣殑琛ㄦ牸鏁版嵁:", tableData.value); + console.log("鏇存柊鍚庣殑閫変腑ID:", selectedIds.value); + + // 鍏抽棴閫夋嫨瀵硅瘽妗� + innerVisible.value = false; + + // 鏄剧ず缁撴灉娑堟伅 + let message = ""; + if (addedCount > 0) { + message += `鎴愬姛娣诲姞 ${addedCount} 鏉℃暟鎹甡; + } + if (duplicateCount > 0) { + message += (message ? "锛�" : "") + `璺宠繃 ${duplicateCount} 鏉¢噸澶嶆暟鎹甡; + } + if (message) { + ElMessage.success(message); + } else { + ElMessage.info("娌℃湁鏂版暟鎹娣诲姞"); + } +}; +const handleSelectionChange = (selection) => { + formalDatabaseSelectedData.value = selection; +}; +const handleReset = () => { + console.log(copyForm.value); + tableData.value = + JSON.parse(JSON.stringify(copyForm.value.productionInventoryList)) || []; + detailsTableData.value = + JSON.parse(JSON.stringify(copyForm.value.productionList)) || []; +}; // 鎻愪氦琛ㄥ崟 const handleSubmit = async () => { - if (!formRef.value) return - - await formRef.value.validate((valid) => { - if (valid) { - loading.value = true - // 瑙﹀彂鎴愬姛浜嬩欢锛屼紶閫掕〃鍗曟暟鎹� - emit('success', { ...formData }) - loading.value = false - handleClose() - } - }) -} + let data = { + productionList: detailsTableData.value, + productionInventoryList: tableData.value, + ...copyForm.value, + }; + let res = await addOrEditPM(data); + if (res.code === 200) { + dialogVisible.value = false; + emit("success"); + } else { + ElMessage.error("鎻愪氦澶辫触"); + } +}; // 鍏抽棴寮圭獥 const handleClose = () => { - dialogVisible.value = false - formRef.value?.resetFields() + dialogVisible.value = false; + formRef.value?.resetFields(); Object.assign(formData, { - category: '', - unit: '', + category: "", + unit: "", productionVolume: 0, laborCost: 0, materialCost: 0, @@ -178,32 +404,129 @@ totalCost: 0, totalPrice: 0, profit: 0, - reviewer: '', - date: '' - }) -} + reviewer: "", + date: "", + }); +}; // 娣诲姞鍗曞厓鏍肩紪杈戝鐞嗗嚱鏁� const handleCellEdit = (row, prop, value) => { - console.log('鍗曞厓鏍肩紪杈�:', prop) - // console.log('鍗曞厓鏍肩紪杈戝畬鎴�:', row, prop, value) - // 杩欓噷鍙互娣诲姞楠岃瘉閫昏緫锛屼緥濡傛鏌ヤ娇鐢ㄩ噺鏄惁澶т簬搴撳瓨 - if (prop === 'used' && Number(value) > Number(row.stock)) { - ElMessage.warning('浣跨敤鏁伴噺涓嶈兘澶т簬搴撳瓨鏁伴噺锛�') - // 鍙互鍦ㄨ繖閲岄噸缃�� - row.used = row.stock + if (prop === "usedQuantity") { + const numValue = Number(value); + const inventory = Number(row.inventoryQuantity); + + // 楠岃瘉杈撳叆鍊� + if (isNaN(numValue) || numValue < 0) { + ElMessage.warning("浣跨敤鏁伴噺蹇呴』鏄潪璐熸暟锛�"); + row.usedQuantity = 0; + return; + } + + if (numValue > inventory) { + ElMessage.warning(`浣跨敤鏁伴噺涓嶈兘澶т簬搴撳瓨鏁伴噺锛�${inventory}锛夛紒`); + row.usedQuantity = inventory; + return; + } + + // 鏇存柊鍊� + row.usedQuantity = numValue; + console.log(`鏇存柊 ${row.coal} 鐨勪娇鐢ㄦ暟閲忎负: ${numValue}`); } -} +}; + +// 澶勭悊鐢熶骇鏄庣粏琛ㄦ牸鐨勬搷浣� +const addNewRow = () => { + detailsTableData.value.push({ + coal: "", + productionQuantity: "", + laborCost: "", + energyConsumptionCost: "", + equipmentDepreciation: "", + purchasePrice: "", + autoCalculate: "0.00", + producer: "", + }); +}; + +const clearAllRows = () => { + detailsTableData.value = []; + ElMessage.success("宸叉竻绌烘墍鏈夋暟鎹�"); +}; + +const handleDetailsChange = (data) => { + console.log("鐢熶骇鏄庣粏鏁版嵁鍙樺寲:", data); +}; + +const handleDeleteRow = (index) => { + ElMessage.success(`宸插垹闄ょ ${index + 1} 琛屾暟鎹甡); +}; + +// 鍒犻櫎鍗曚釜宸查�夋暟鎹」 +const handleRemoveItem = (row) => { + console.log("鍒犻櫎椤�:", row); + const index = tableData.value.findIndex( + (item) => item.officialId === row.officialId + ); + if (index > -1) { + tableData.value.splice(index, 1); + + // 鏇存柊selectedIds + const updatedOfficialIds = tableData.value + .map((item) => item.officialId) + .filter((id) => id); + selectedIds.value = updatedOfficialIds; + + console.log("鍒犻櫎鍚庣殑琛ㄦ牸鏁版嵁:", tableData.value); + console.log("鏇存柊鍚庣殑閫変腑ID:", selectedIds.value); + + ElMessage.success("宸插垹闄ら�変腑椤�"); + } +}; + +// 娓呯┖鎵�鏈夊凡閫夋暟鎹� +const removeSelectedData = () => { + if (tableData.value.length === 0) { + ElMessage.warning("娌℃湁鍙竻绌虹殑鏁版嵁"); + return; + } + + ElMessageBox.confirm("纭娓呯┖鎵�鏈夊凡閫夋嫨鐨勬暟鎹悧锛�", "璀﹀憡", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning", + }) + .then(() => { + tableData.value = []; + selectedIds.value = []; + console.log("宸叉竻绌烘墍鏈夊凡閫夋暟鎹�"); + ElMessage.success("宸叉竻绌烘墍鏈夋暟鎹�"); + }) + .catch(() => { + console.log("鍙栨秷娓呯┖鎿嶄綔"); + }); +}; + +// 璁$畻鎬讳娇鐢ㄩ噺 +const totalUsedQuantity = computed(() => { + return tableData.value.reduce((total, item) => { + const usedQty = Number(item.usedQuantity) || 0; + return total + usedQty; + }, 0); +}); </script> -<style scoped lang="scss"> -.el-form{ +<style scoped lang="scss"> +.el-form { .el-row { padding-top: 20px; - background: rgba($color: #F8FAFB, $alpha: 0.5) ; + background: rgba($color: #f8fafb, $alpha: 0.5); } } -.el-row>.el-col>h1{ + +.el-row > .el-col > h1 { font-weight: bolder; } -</style> \ No newline at end of file +.empty-table > .el-row { + margin-bottom: 12px; +} +</style> -- Gitblit v1.9.3