From ea6ad9ddc3d5b33897e93276282245f7023836ff Mon Sep 17 00:00:00 2001 From: spring <2396852758@qq.com> Date: 星期四, 28 八月 2025 17:45:28 +0800 Subject: [PATCH] 大数据市场分析 --- src/views/sales/components/GenerateReturnDialog.vue | 528 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 528 insertions(+), 0 deletions(-) diff --git a/src/views/sales/components/GenerateReturnDialog.vue b/src/views/sales/components/GenerateReturnDialog.vue new file mode 100644 index 0000000..ff6a7bd --- /dev/null +++ b/src/views/sales/components/GenerateReturnDialog.vue @@ -0,0 +1,528 @@ +<template> + <el-dialog + :model-value="dialogGenerateVisible" + @update:model-value="$emit('update:dialogGenerateVisible', $event)" + title="涓�閿敓鎴愰��璐у崟" + width="1000px" + :close-on-click-modal="false" + > + <div class="generate-container"> + <!-- 閫夋嫨閲囪喘璁㈠崟 --> + <el-card class="select-card" shadow="never"> + <template #header> + <div class="card-header"> + <span>閫夋嫨閲囪喘璁㈠崟</span> + </div> + </template> + + <el-form :inline="true" :model="searchForm" class="search-form"> + <el-form-item label="渚涘簲鍟�"> + <el-select + v-model="searchForm.supplierId" + placeholder="璇烽�夋嫨渚涘簲鍟�" + clearable + style="width: 200px" + @change="handleSupplierChange" + > + <el-option + :label="item.label" + v-for="item in supplierList" + :key="item.value" + :value="item.value" + /> + </el-select> + </el-form-item> + <el-form-item label="璁㈠崟鐘舵��"> + <el-select + v-model="searchForm.orderStatus" + placeholder="璇烽�夋嫨璁㈠崟鐘舵��" + clearable + style="width: 150px" + > + <el-option + :label="item.label" + v-for="item in orderStatusList" + :key="item.value" + :value="item.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" @click="searchOrders">鏌ヨ</el-button> + <el-button @click="resetSearch">閲嶇疆</el-button> + </el-form-item> + </el-form> + + <el-table + :data="orderList" + @selection-change="handleOrderSelectionChange" + border + style="width: 100%" + max-height="300" + > + <el-table-column type="selection" width="55" /> + <el-table-column label="璁㈠崟鍙�" prop="orderNo" width="180" /> + <el-table-column label="渚涘簲鍟�" prop="supplierName" width="150" /> + <el-table-column label="璁㈠崟鏃ユ湡" prop="orderDate" width="120" /> + <el-table-column label="鍟嗗搧鍚嶇О" prop="coalName" width="150" /> + <el-table-column label="璁㈠崟鏁伴噺" prop="orderQuantity" width="100"> + <template #default="scope"> + {{ scope.row.orderQuantity }} 鍚� + </template> + </el-table-column> + <el-table-column label="宸叉敹璐ф暟閲�" prop="receivedQuantity" width="100"> + <template #default="scope"> + {{ scope.row.receivedQuantity }} 鍚� + </template> + </el-table-column> + <el-table-column label="鐘舵��" prop="status" width="100"> + <template #default="scope"> + <el-tag :type="getOrderStatusType(scope.row.status)"> + {{ getOrderStatusText(scope.row.status) }} + </el-tag> + </template> + </el-table-column> + </el-table> + </el-card> + + <!-- 閫�璐т俊鎭厤缃� --> + <el-card class="config-card" shadow="never"> + <template #header> + <div class="card-header"> + <span>閫�璐т俊鎭厤缃�</span> + </div> + </template> + + <el-form :model="returnConfig" label-width="120px" class="config-form"> + <el-row :gutter="20"> + <el-col :span="12"> + <el-form-item label="閫�璐у師鍥�" prop="returnReason"> + <el-select + v-model="returnConfig.returnReason" + placeholder="璇烽�夋嫨閫�璐у師鍥�" + style="width: 100%" + filterable + allow-create + > + <el-option + :label="item.label" + v-for="item in returnReasonList" + :key="item.value" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="12"> + <el-form-item label="鎿嶄綔鍛�" prop="operatorId"> + <el-select + v-model="returnConfig.operatorId" + placeholder="璇烽�夋嫨鎿嶄綔鍛�" + style="width: 100%" + filterable + > + <el-option + :label="item.label" + v-for="item in operatorList" + :key="item.value" + :value="item.value" + /> + </el-select> + </el-form-item> + </el-col> + </el-row> + + <el-form-item label="澶囨敞" prop="remark"> + <el-input + v-model="returnConfig.remark" + type="textarea" + :rows="3" + placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�" + /> + </el-form-item> + </el-form> + </el-card> + + <!-- 棰勮閫�璐у崟 --> + <el-card class="preview-card" shadow="never"> + <template #header> + <div class="card-header"> + <span>棰勮閫�璐у崟</span> + </div> + </template> + + <div v-if="selectedOrders.length === 0" class="no-selection"> + <el-empty description="璇峰厛閫夋嫨瑕侀��璐х殑閲囪喘璁㈠崟" /> + </div> + + <div v-else class="preview-content"> + <el-table :data="previewReturnItems" border style="width: 100%"> + <el-table-column label="璁㈠崟鍙�" prop="orderNo" width="150" /> + <el-table-column label="鍟嗗搧鍚嶇О" prop="coalName" width="150" /> + <el-table-column label="閫�璐ф暟閲�" width="120"> + <template #default="scope"> + <el-input + v-model.number="scope.row.returnQuantity" + placeholder="閫�璐ф暟閲�" + type="number" + @input="updateReturnQuantity(scope.$index)" + > + <template v-slot:suffix> + <span>鍚�</span> + </template> + </el-input> + </template> + </el-table-column> + <el-table-column label="鍗曚环" prop="unitPrice" width="120"> + <template #default="scope"> + {{ scope.row.unitPrice }} 鍏�/鍚� + </template> + </el-table-column> + <el-table-column label="灏忚" width="120"> + <template #default="scope"> + {{ (scope.row.returnQuantity * scope.row.unitPrice).toFixed(2) }} 鍏� + </template> + </el-table-column> + </el-table> + + <div class="preview-summary"> + <span class="summary-item"> + 鎬绘暟閲忥細<strong>{{ getTotalReturnQuantity() }} 鍚�</strong> + </span> + <span class="summary-item"> + 鎬婚噾棰濓細<strong>{{ getTotalReturnAmount() }} 鍏�</strong> + </span> + </div> + </div> + </el-card> + </div> + + <template #footer> + <div class="dialog-footer"> + <el-button @click="handleClose">鍙栨秷</el-button> + <el-button + type="primary" + @click="generateReturnOrder" + :loading="generateLoading" + :disabled="selectedOrders.length === 0" + > + 鐢熸垚閫�璐у崟 + </el-button> + </div> + </template> + </el-dialog> +</template> + +<script setup> +import { ref, reactive, computed, watch } from "vue"; +import { ElMessage } from "element-plus"; + +// Props +const props = defineProps({ + dialogGenerateVisible: { + type: Boolean, + default: false + } +}); + +// Emits +const emit = defineEmits(['update:dialogGenerateVisible', 'success']); + +// 鍝嶅簲寮忔暟鎹� +const searchForm = reactive({ + supplierId: "", + orderStatus: "" +}); + +const returnConfig = reactive({ + returnReason: "", + operatorId: "", + remark: "" +}); + +const orderList = ref([]); +const selectedOrders = ref([]); +const generateLoading = ref(false); + +// 渚涘簲鍟嗗垪琛� +const supplierList = ref([ + { value: "1", label: "渚涘簲鍟咥" }, + { value: "2", label: "渚涘簲鍟咮" }, + { value: "3", label: "渚涘簲鍟咰" } +]); + +// 璁㈠崟鐘舵�佸垪琛� +const orderStatusList = ref([ + { value: "received", label: "宸叉敹璐�" }, + { value: "partial_received", label: "閮ㄥ垎鏀惰揣" }, + { value: "quality_issue", label: "璐ㄩ噺闂" } +]); + +// 閫�璐у師鍥犲垪琛� +const returnReasonList = ref([ + { value: "璐ㄩ噺涓嶅悎鏍�", label: "璐ㄩ噺涓嶅悎鏍�" }, + { value: "浜よ揣婊炲悗", label: "浜よ揣婊炲悗" }, + { value: "瑙勬牸涓嶇", label: "瑙勬牸涓嶇" }, + { value: "鏁伴噺涓嶇", label: "鏁伴噺涓嶇" }, + { value: "鍏朵粬鍘熷洜", label: "鍏朵粬鍘熷洜" } +]); + +// 鎿嶄綔鍛樺垪琛� +const operatorList = ref([ + { value: "1", label: "闄堝織寮�" }, + { value: "2", label: "鍒樼編鐜�" }, + { value: "3", label: "鐜嬪缓鍥�" } +]); + +// 妯℃嫙閲囪喘璁㈠崟鏁版嵁 +const mockOrderData = [ + { + id: "1", + orderNo: "CG20241201001", + supplierName: "渚涘簲鍟咥", + orderDate: "2024-12-01", + coalName: "鏃犵儫鐓�", + orderQuantity: 100, + receivedQuantity: 80, + status: "partial_received", + unitPrice: 800 + }, + { + id: "2", + orderNo: "CG20241201002", + supplierName: "渚涘簲鍟咥", + orderDate: "2024-12-01", + coalName: "鐑熺叅", + orderQuantity: 50, + receivedQuantity: 50, + status: "quality_issue", + unitPrice: 750 + }, + { + id: "3", + orderNo: "CG20241201003", + supplierName: "渚涘簲鍟咮", + orderDate: "2024-12-01", + coalName: "瑜愮叅", + orderQuantity: 80, + receivedQuantity: 60, + status: "partial_received", + unitPrice: 600 + } +]; + +// 鑾峰彇璁㈠崟鐘舵�佺被鍨� +const getOrderStatusType = (status) => { + const statusMap = { + received: "success", + partial_received: "warning", + quality_issue: "danger" + }; + return statusMap[status] || ""; +}; + +// 鑾峰彇璁㈠崟鐘舵�佹枃鏈� +const getOrderStatusText = (status) => { + const statusMap = { + received: "宸叉敹璐�", + partial_received: "閮ㄥ垎鏀惰揣", + quality_issue: "璐ㄩ噺闂" + }; + return statusMap[status] || status; +}; + +// 渚涘簲鍟嗗彉鍖栧鐞� +const handleSupplierChange = () => { + searchOrders(); +}; + +// 鏌ヨ璁㈠崟 +const searchOrders = () => { + // 妯℃嫙API璋冪敤 + orderList.value = mockOrderData.filter(order => { + if (searchForm.supplierId && order.supplierName !== supplierList.value.find(s => s.value === searchForm.supplierId)?.label) { + return false; + } + if (searchForm.orderStatus && order.status !== searchForm.orderStatus) { + return false; + } + return true; + }); +}; + +// 閲嶇疆鎼滅储 +const resetSearch = () => { + Object.assign(searchForm, { + supplierId: "", + orderStatus: "" + }); + searchOrders(); +}; + +// 璁㈠崟閫夋嫨鍙樺寲 +const handleOrderSelectionChange = (selection) => { + selectedOrders.value = selection; +}; + +// 棰勮閫�璐у晢鍝� +const previewReturnItems = computed(() => { + return selectedOrders.value.map(order => ({ + ...order, + returnQuantity: order.status === 'quality_issue' ? order.receivedQuantity : (order.orderQuantity - order.receivedQuantity) + })); +}); + +// 鏇存柊閫�璐ф暟閲� +const updateReturnQuantity = (index) => { + const item = previewReturnItems.value[index]; + if (item.returnQuantity > item.receivedQuantity) { + item.returnQuantity = item.receivedQuantity; + ElMessage.warning("閫�璐ф暟閲忎笉鑳借秴杩囧凡鏀惰揣鏁伴噺"); + } +}; + +// 璁$畻鎬婚��璐ф暟閲� +const getTotalReturnQuantity = () => { + return previewReturnItems.value.reduce((total, item) => total + (item.returnQuantity || 0), 0); +}; + +// 璁$畻鎬婚��璐ч噾棰� +const getTotalReturnAmount = () => { + return previewReturnItems.value.reduce((total, item) => { + return total + ((item.returnQuantity || 0) * item.unitPrice); + }, 0).toFixed(2); +}; + +// 鐢熸垚閫�璐у崟 +const generateReturnOrder = async () => { + if (!returnConfig.returnReason) { + ElMessage.warning("璇烽�夋嫨閫�璐у師鍥�"); + return; + } + if (!returnConfig.operatorId) { + ElMessage.warning("璇烽�夋嫨鎿嶄綔鍛�"); + return; + } + + generateLoading.value = true; + + try { + // 妯℃嫙鐢熸垚閫�璐у崟 + await new Promise(resolve => setTimeout(resolve, 1000)); + + const returnOrder = { + returnNo: `TH${Date.now()}`, + supplierName: selectedOrders.value[0]?.supplierName, + returnDate: new Date().toISOString().split('T')[0], + operatorName: operatorList.value.find(op => op.value === returnConfig.operatorId)?.label, + returnReason: returnConfig.returnReason, + returnQuantity: getTotalReturnQuantity(), + returnAmount: getTotalReturnAmount(), + status: "draft", + createTime: new Date().toLocaleString(), + remark: returnConfig.remark, + returnItems: previewReturnItems.value.map(item => ({ + coalId: item.id, + coalName: item.coalName, + specification: "鏍囧噯瑙勬牸", + quantity: item.returnQuantity, + unitPrice: item.unitPrice + })) + }; + + ElMessage.success("閫�璐у崟鐢熸垚鎴愬姛"); + emit('success', returnOrder); + handleClose(); + } catch (error) { + ElMessage.error("鐢熸垚閫�璐у崟澶辫触"); + } finally { + generateLoading.value = false; + } +}; + +// 鍏抽棴瀵硅瘽妗� +const handleClose = () => { + emit('update:dialogGenerateVisible', false); + // 閲嶇疆鏁版嵁 + Object.assign(searchForm, { + supplierId: "", + orderStatus: "" + }); + Object.assign(returnConfig, { + returnReason: "", + operatorId: "", + remark: "" + }); + selectedOrders.value = []; + orderList.value = mockOrderData; +}; + +// 鍒濆鍖栨暟鎹� +watch(() => props.dialogGenerateVisible, (visible) => { + if (visible) { + orderList.value = mockOrderData; + } +}, { immediate: true }); +</script> + +<style scoped> +.generate-container { + padding: 0; +} + +.select-card, +.config-card, +.preview-card { + margin-bottom: 20px; +} + +.select-card:last-child, +.config-card:last-child, +.preview-card:last-child { + margin-bottom: 0; +} + +.card-header { + font-weight: bold; + font-size: 16px; +} + +.search-form { + margin-bottom: 20px; +} + +.config-form { + padding: 20px 0; +} + +.no-selection { + text-align: center; + padding: 40px 0; +} + +.preview-content { + padding: 20px 0; +} + +.preview-summary { + margin-top: 15px; + text-align: right; + padding: 10px; + background-color: #f5f7fa; + border-radius: 4px; +} + +.summary-item { + margin-left: 20px; + font-size: 14px; +} + +.summary-item strong { + color: #409eff; + font-size: 16px; +} + +.dialog-footer { + text-align: right; +} +</style> -- Gitblit v1.9.3