¶Ô±ÈÐÂÎļþ |
| | |
| | | <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: "ä¾åºåA" }, |
| | | { value: "2", label: "ä¾åºåB" }, |
| | | { value: "3", label: "ä¾åºåC" } |
| | | ]); |
| | | |
| | | // 订åç¶æå表 |
| | | 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: "ä¾åºåA", |
| | | orderDate: "2024-12-01", |
| | | coalName: "æ çç
¤", |
| | | orderQuantity: 100, |
| | | receivedQuantity: 80, |
| | | status: "partial_received", |
| | | unitPrice: 800 |
| | | }, |
| | | { |
| | | id: "2", |
| | | orderNo: "CG20241201002", |
| | | supplierName: "ä¾åºåA", |
| | | orderDate: "2024-12-01", |
| | | coalName: "çç
¤", |
| | | orderQuantity: 50, |
| | | receivedQuantity: 50, |
| | | status: "quality_issue", |
| | | unitPrice: 750 |
| | | }, |
| | | { |
| | | id: "3", |
| | | orderNo: "CG20241201003", |
| | | supplierName: "ä¾åºåB", |
| | | 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> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog |
| | | :model-value="dialogFormVisible" |
| | | @update:model-value="$emit('update:dialogFormVisible', $event)" |
| | | :title="title" |
| | | width="1000px" |
| | | :close-on-click-modal="false" |
| | | @close="handleClose" |
| | | > |
| | | <el-form |
| | | ref="formRef" |
| | | :model="formData" |
| | | :rules="rules" |
| | | label-width="120px" |
| | | class="purchase-return-form" |
| | | > |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="ä¾åºå" prop="supplierId"> |
| | | <el-select |
| | | v-model="formData.supplierId" |
| | | placeholder="è¯·éæ©ä¾åºå" |
| | | style="width: 100%" |
| | | filterable |
| | | > |
| | | <el-option |
| | | :label="item.label" |
| | | v-for="item in supplierList" |
| | | :key="item.value" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="åæ®æ¥æ" prop="returnDate"> |
| | | <el-date-picker |
| | | v-model="formData.returnDate" |
| | | type="date" |
| | | placeholder="è¯·éæ©åæ®æ¥æ" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="æä½å" prop="operatorId"> |
| | | <el-select |
| | | v-model="formData.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-col :span="12"> |
| | | <el-form-item label="éè´§åå " prop="returnReason"> |
| | | <el-select |
| | | v-model="formData.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-row> |
| | | |
| | | <el-row :gutter="20"> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§æ°é" prop="returnQuantity"> |
| | | <el-input |
| | | v-model.number="formData.returnQuantity" |
| | | placeholder="请è¾å
¥éè´§æ°é" |
| | | style="width: 100%" |
| | | > |
| | | <template v-slot:suffix> |
| | | <span>å¨</span> |
| | | </template> |
| | | </el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="éè´§éé¢" prop="returnAmount"> |
| | | <el-input |
| | | v-model.number="formData.returnAmount" |
| | | placeholder="请è¾å
¥éè´§éé¢" |
| | | style="width: 100%" |
| | | > |
| | | <template v-slot:suffix> |
| | | <span>å
</span> |
| | | </template> |
| | | </el-input> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | |
| | | <el-form-item label="éè´§ååä¿¡æ¯" prop="returnItems"> |
| | | <div class="return-items-container"> |
| | | <div class="return-items-header"> |
| | | <span>ååæç»</span> |
| | | <el-button type="primary" size="small" @click="addReturnItem"> |
| | | æ·»å åå |
| | | </el-button> |
| | | </div> |
| | | <el-table :data="formData.returnItems" border style="width: 100%"> |
| | | <el-table-column label="åååç§°" width="180"> |
| | | <template #default="scope"> |
| | | <el-select |
| | | v-model="scope.row.coalId" |
| | | placeholder="è¯·éæ©åå" |
| | | style="width: 100%" |
| | | filterable |
| | | > |
| | | <el-option |
| | | :label="item.label" |
| | | v-for="item in coalList" |
| | | :key="item.value" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="è§æ ¼åå·" width="120"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-model="scope.row.specification" |
| | | placeholder="è§æ ¼åå·" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æ°é" width="120"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-model.number="scope.row.quantity" |
| | | placeholder="æ°é" |
| | | type="number" |
| | | > |
| | | <template v-slot:suffix> |
| | | <span>å¨</span> |
| | | </template> |
| | | </el-input> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä»·" width="120"> |
| | | <template #default="scope"> |
| | | <el-input |
| | | v-model.number="scope.row.unitPrice" |
| | | placeholder="åä»·" |
| | | type="number" |
| | | > |
| | | <template v-slot:suffix> |
| | | <span>å
/å¨</span> |
| | | </template> |
| | | </el-input> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å°è®¡" width="120"> |
| | | <template #default="scope"> |
| | | <span>{{ (scope.row.quantity * scope.row.unitPrice).toFixed(2) }} å
</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="æä½" width="80"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | @click="removeReturnItem(scope.$index)" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | </el-form-item> |
| | | |
| | | <el-form-item label="夿³¨" prop="remark"> |
| | | <el-input |
| | | v-model="formData.remark" |
| | | type="textarea" |
| | | :rows="3" |
| | | placeholder="请è¾å
¥å¤æ³¨ä¿¡æ¯" |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="handleClose">åæ¶</el-button> |
| | | <el-button type="primary" @click="handleSubmit" :loading="submitLoading"> |
| | | æäº¤å®¡æ ¸ |
| | | </el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, watch, nextTick } from "vue"; |
| | | import { ElMessage } from "element-plus"; |
| | | |
| | | // Props |
| | | const props = defineProps({ |
| | | dialogFormVisible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | form: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: "" |
| | | }, |
| | | isEdit: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }); |
| | | |
| | | // Emits |
| | | const emit = defineEmits(['update:dialogFormVisible', 'update:form', 'submit', 'success']); |
| | | |
| | | // ååºå¼æ°æ® |
| | | const formRef = ref(null); |
| | | const submitLoading = ref(false); |
| | | |
| | | // è¡¨åæ°æ® |
| | | const formData = reactive({ |
| | | supplierId: "", |
| | | returnDate: "", |
| | | operatorId: "", |
| | | returnReason: "", |
| | | returnQuantity: "", |
| | | returnAmount: "", |
| | | returnItems: [], |
| | | remark: "" |
| | | }); |
| | | |
| | | // åå§åè¡¨åæ°æ® |
| | | const initFormData = () => { |
| | | Object.assign(formData, { |
| | | supplierId: "", |
| | | returnDate: "", |
| | | operatorId: "", |
| | | returnReason: "", |
| | | returnQuantity: "", |
| | | returnAmount: "", |
| | | returnItems: [], |
| | | remark: "" |
| | | }); |
| | | }; |
| | | |
| | | // ä¾åºåå表 |
| | | const supplierList = ref([ |
| | | { value: "1", label: "ä¾åºåA" }, |
| | | { value: "2", label: "ä¾åºåB" }, |
| | | { value: "3", label: "ä¾åºåC" } |
| | | ]); |
| | | |
| | | // æä½åå表 |
| | | const operatorList = ref([ |
| | | { value: "1", label: "éå¿å¼º" }, |
| | | { value: "2", label: "åç¾ç²" }, |
| | | { value: "3", label: "ç建å½" } |
| | | ]); |
| | | |
| | | // éè´§åå å表 |
| | | const returnReasonList = ref([ |
| | | { value: "è´¨éä¸åæ ¼", label: "è´¨éä¸åæ ¼" }, |
| | | { value: "交货æ»å", label: "交货æ»å" }, |
| | | { value: "è§æ ¼ä¸ç¬¦", label: "è§æ ¼ä¸ç¬¦" }, |
| | | { value: "æ°éä¸ç¬¦", label: "æ°éä¸ç¬¦" }, |
| | | { value: "å
¶ä»åå ", label: "å
¶ä»åå " } |
| | | ]); |
| | | |
| | | // ååå表 |
| | | const coalList = ref([ |
| | | { value: "1", label: "æ çç
¤" }, |
| | | { value: "2", label: "çç
¤" }, |
| | | { value: "3", label: "è¤ç
¤" }, |
| | | { value: "4", label: "ç¦ç
¤" } |
| | | ]); |
| | | |
| | | // 表åéªè¯è§å |
| | | const rules = { |
| | | supplierId: [ |
| | | { required: true, message: "è¯·éæ©ä¾åºå", trigger: "change" } |
| | | ], |
| | | returnDate: [ |
| | | { required: true, message: "è¯·éæ©åæ®æ¥æ", trigger: "change" } |
| | | ], |
| | | operatorId: [ |
| | | { required: true, message: "è¯·éæ©æä½å", trigger: "change" } |
| | | ], |
| | | returnReason: [ |
| | | { required: true, message: "è¯·éæ©éè´§åå ", trigger: "change" } |
| | | ], |
| | | returnQuantity: [ |
| | | { required: true, message: "请è¾å
¥éè´§æ°é", trigger: "blur" }, |
| | | { type: "number", min: 0, message: "æ°éå¿
须大äº0", trigger: "blur" } |
| | | ], |
| | | returnItems: [ |
| | | { |
| | | type: "array", |
| | | required: true, |
| | | message: "请è³å°æ·»å ä¸ä¸ªéè´§åå", |
| | | trigger: "change", |
| | | validator: (rule, value, callback) => { |
| | | if (!value || value.length === 0) { |
| | | callback(new Error("请è³å°æ·»å ä¸ä¸ªéè´§åå")); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | ] |
| | | }; |
| | | |
| | | // çå¬è¡¨åæ°æ®åå |
| | | watch(() => props.form, (newVal) => { |
| | | Object.assign(formData, newVal); |
| | | if (!formData.returnItems || formData.returnItems.length === 0) { |
| | | formData.returnItems = []; |
| | | } |
| | | }, { deep: true, immediate: true }); |
| | | |
| | | // æ·»å éè´§åå |
| | | const addReturnItem = () => { |
| | | formData.returnItems.push({ |
| | | coalId: "", |
| | | specification: "", |
| | | quantity: 0, |
| | | unitPrice: 0 |
| | | }); |
| | | }; |
| | | |
| | | // å é¤éè´§åå |
| | | const removeReturnItem = (index) => { |
| | | formData.returnItems.splice(index, 1); |
| | | }; |
| | | |
| | | // å
³éå¯¹è¯æ¡ |
| | | const handleClose = () => { |
| | | emit('update:dialogFormVisible', false); |
| | | formRef.value?.resetFields(); |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const handleSubmit = async () => { |
| | | if (!formRef.value) return; |
| | | |
| | | try { |
| | | await formRef.value.validate(); |
| | | |
| | | // éªè¯éè´§ååä¿¡æ¯ |
| | | if (formData.returnItems.length === 0) { |
| | | ElMessage.warning("请è³å°æ·»å ä¸ä¸ªéè´§åå"); |
| | | return; |
| | | } |
| | | |
| | | for (let item of formData.returnItems) { |
| | | if (!item.coalId) { |
| | | ElMessage.warning("è¯·éæ©åå"); |
| | | return; |
| | | } |
| | | if (!item.quantity || item.quantity <= 0) { |
| | | ElMessage.warning("请è¾å
¥ææçååæ°é"); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | submitLoading.value = true; |
| | | |
| | | // 模ææäº¤ |
| | | setTimeout(() => { |
| | | submitLoading.value = false; |
| | | ElMessage.success("æäº¤æå"); |
| | | emit('submit', { ...formData }); |
| | | handleClose(); |
| | | }, 1000); |
| | | |
| | | } catch (error) { |
| | | console.error('表åéªè¯å¤±è´¥:', error); |
| | | } |
| | | }; |
| | | |
| | | // è®¡ç®æ»æ°é |
| | | const calculateTotalQuantity = () => { |
| | | return formData.returnItems.reduce((total, item) => total + (item.quantity || 0), 0); |
| | | }; |
| | | |
| | | // è®¡ç®æ»éé¢ |
| | | const calculateTotalAmount = () => { |
| | | return formData.returnItems.reduce((total, item) => { |
| | | return total + ((item.quantity || 0) * (item.unitPrice || 0)); |
| | | }, 0); |
| | | }; |
| | | |
| | | // çå¬éè´§ååååï¼èªå¨è®¡ç®æ»æ°éåæ»éé¢ |
| | | watch(() => formData.returnItems, () => { |
| | | formData.returnQuantity = calculateTotalQuantity(); |
| | | formData.returnAmount = calculateTotalAmount(); |
| | | }, { deep: true }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .purchase-return-form { |
| | | padding: 20px 0; |
| | | } |
| | | |
| | | .return-items-container { |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | padding: 15px; |
| | | } |
| | | |
| | | .return-items-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | margin-bottom: 15px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .dialog-footer { |
| | | text-align: right; |
| | | } |
| | | |
| | | .el-table { |
| | | margin-top: 10px; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <el-dialog |
| | | :model-value="dialogViewVisible" |
| | | @update:model-value="$emit('update:dialogViewVisible', $event)" |
| | | :title="title" |
| | | width="900px" |
| | | :close-on-click-modal="false" |
| | | > |
| | | <div class="view-container"> |
| | | <!-- åºæ¬ä¿¡æ¯ --> |
| | | <el-card class="info-card" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>åºæ¬ä¿¡æ¯</span> |
| | | </div> |
| | | </template> |
| | | <el-descriptions :column="2" border> |
| | | <el-descriptions-item label="éè´§åå·"> |
| | | {{ form.returnNo || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ä¾åºå"> |
| | | {{ form.supplierName || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="åæ®æ¥æ"> |
| | | {{ form.returnDate || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="æä½å"> |
| | | {{ form.operatorName || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éè´§åå "> |
| | | {{ form.returnReason || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="ç¶æ"> |
| | | <el-tag :type="getStatusType(form.status)"> |
| | | {{ getStatusText(form.status) }} |
| | | </el-tag> |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éè´§æ°é"> |
| | | {{ form.returnQuantity || 0 }} å¨ |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="éè´§éé¢"> |
| | | {{ form.returnAmount || 0 }} å
|
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="å建æ¶é´" :span="2"> |
| | | {{ form.createTime || '-' }} |
| | | </el-descriptions-item> |
| | | <el-descriptions-item label="夿³¨" :span="2"> |
| | | {{ form.remark || '-' }} |
| | | </el-descriptions-item> |
| | | </el-descriptions> |
| | | </el-card> |
| | | |
| | | <!-- éè´§ååæç» --> |
| | | <el-card class="info-card" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>éè´§ååæç»</span> |
| | | </div> |
| | | </template> |
| | | <el-table :data="form.returnItems || []" border style="width: 100%"> |
| | | <el-table-column label="åºå·" type="index" width="60" /> |
| | | <el-table-column label="åååç§°" prop="coalName" width="150" /> |
| | | <el-table-column label="è§æ ¼åå·" prop="specification" width="150" /> |
| | | <el-table-column label="æ°é" prop="quantity" width="100"> |
| | | <template #default="scope"> |
| | | {{ scope.row.quantity || 0 }} å¨ |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="åä»·" prop="unitPrice" width="120"> |
| | | <template #default="scope"> |
| | | {{ scope.row.unitPrice || 0 }} å
/å¨ |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å°è®¡" width="120"> |
| | | <template #default="scope"> |
| | | {{ ((scope.row.quantity || 0) * (scope.row.unitPrice || 0)).toFixed(2) }} å
|
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | <div class="table-summary"> |
| | | <span class="summary-item"> |
| | | æ»æ°éï¼<strong>{{ getTotalQuantity() }} å¨</strong> |
| | | </span> |
| | | <span class="summary-item"> |
| | | æ»éé¢ï¼<strong>{{ getTotalAmount() }} å
</strong> |
| | | </span> |
| | | </div> |
| | | </el-card> |
| | | |
| | | <!-- å®¡æ¹æµç¨ --> |
| | | <el-card class="info-card" shadow="never"> |
| | | <template #header> |
| | | <div class="card-header"> |
| | | <span>å®¡æ¹æµç¨</span> |
| | | </div> |
| | | </template> |
| | | <el-timeline> |
| | | <el-timeline-item |
| | | v-for="(activity, index) in approvalFlow" |
| | | :key="index" |
| | | :timestamp="activity.timestamp" |
| | | :type="activity.type" |
| | | > |
| | | <h4>{{ activity.title }}</h4> |
| | | <p>{{ activity.content }}</p> |
| | | <p v-if="activity.operator">æä½äººï¼{{ activity.operator }}</p> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </el-card> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button @click="handleClose">å
³é</el-button> |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { computed } from "vue"; |
| | | |
| | | // Props |
| | | const props = defineProps({ |
| | | dialogViewVisible: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | form: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: "éè´§å详æ
" |
| | | } |
| | | }); |
| | | |
| | | // Emits |
| | | const emit = defineEmits(['update:dialogViewVisible']); |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | draft: "", |
| | | pending: "warning", |
| | | approved: "success", |
| | | rejected: "danger", |
| | | completed: "info" |
| | | }; |
| | | return statusMap[status] || ""; |
| | | }; |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | draft: "è稿", |
| | | pending: "å¾
å®¡æ ¸", |
| | | approved: "å·²å®¡æ ¸", |
| | | rejected: "å·²æç»", |
| | | completed: "已宿" |
| | | }; |
| | | return statusMap[status] || status; |
| | | }; |
| | | |
| | | // è®¡ç®æ»æ°é |
| | | const getTotalQuantity = () => { |
| | | if (!props.form.returnItems || props.form.returnItems.length === 0) { |
| | | return 0; |
| | | } |
| | | return props.form.returnItems.reduce((total, item) => total + (item.quantity || 0), 0); |
| | | }; |
| | | |
| | | // è®¡ç®æ»éé¢ |
| | | const getTotalAmount = () => { |
| | | if (!props.form.returnItems || props.form.returnItems.length === 0) { |
| | | return 0; |
| | | } |
| | | return props.form.returnItems.reduce((total, item) => { |
| | | return total + ((item.quantity || 0) * (item.unitPrice || 0)); |
| | | }, 0).toFixed(2); |
| | | }; |
| | | |
| | | // å®¡æ¹æµç¨æ°æ® |
| | | const approvalFlow = computed(() => { |
| | | const flow = []; |
| | | |
| | | // å建 |
| | | flow.push({ |
| | | title: "å建éè´§å", |
| | | content: "éè´§åå·²å建ï¼çå¾
æäº¤å®¡æ ¸", |
| | | timestamp: props.form.createTime || new Date().toLocaleString(), |
| | | operator: props.form.operatorName || "ç³»ç»", |
| | | type: "primary" |
| | | }); |
| | | |
| | | // æ ¹æ®ç¶ææ·»å å®¡æ¹æµç¨ |
| | | if (props.form.status === "pending") { |
| | | flow.push({ |
| | | title: "æäº¤å®¡æ ¸", |
| | | content: "éè´§åå·²æäº¤ï¼çå¾
å®¡æ ¸", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: props.form.operatorName || "ç³»ç»", |
| | | type: "warning" |
| | | }); |
| | | } else if (props.form.status === "approved") { |
| | | flow.push({ |
| | | title: "æäº¤å®¡æ ¸", |
| | | content: "éè´§åå·²æäº¤ï¼çå¾
å®¡æ ¸", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: props.form.operatorName || "ç³»ç»", |
| | | type: "warning" |
| | | }); |
| | | flow.push({ |
| | | title: "å®¡æ ¸éè¿", |
| | | content: "éè´§åå®¡æ ¸éè¿", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: "å®¡æ ¸å", |
| | | type: "success" |
| | | }); |
| | | } else if (props.form.status === "rejected") { |
| | | flow.push({ |
| | | title: "æäº¤å®¡æ ¸", |
| | | content: "éè´§åå·²æäº¤ï¼çå¾
å®¡æ ¸", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: props.form.operatorName || "ç³»ç»", |
| | | type: "warning" |
| | | }); |
| | | flow.push({ |
| | | title: "å®¡æ ¸æç»", |
| | | content: "éè´§åå®¡æ ¸è¢«æç»", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: "å®¡æ ¸å", |
| | | type: "danger" |
| | | }); |
| | | } else if (props.form.status === "completed") { |
| | | flow.push({ |
| | | title: "æäº¤å®¡æ ¸", |
| | | content: "éè´§åå·²æäº¤ï¼çå¾
å®¡æ ¸", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: props.form.operatorName || "ç³»ç»", |
| | | type: "warning" |
| | | }); |
| | | flow.push({ |
| | | title: "å®¡æ ¸éè¿", |
| | | content: "éè´§åå®¡æ ¸éè¿", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: "å®¡æ ¸å", |
| | | type: "success" |
| | | }); |
| | | flow.push({ |
| | | title: "éè´§å®æ", |
| | | content: "éè´§æµç¨å·²å®æ", |
| | | timestamp: new Date().toLocaleString(), |
| | | operator: "ç³»ç»", |
| | | type: "info" |
| | | }); |
| | | } |
| | | |
| | | return flow; |
| | | }); |
| | | |
| | | // å
³éå¯¹è¯æ¡ |
| | | const handleClose = () => { |
| | | emit('update:dialogViewVisible', false); |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .view-container { |
| | | padding: 0; |
| | | } |
| | | |
| | | .info-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .info-card:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .card-header { |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | } |
| | | |
| | | .table-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; |
| | | } |
| | | |
| | | .el-timeline { |
| | | padding: 20px; |
| | | } |
| | | |
| | | .el-timeline-item h4 { |
| | | margin: 0 0 8px 0; |
| | | font-size: 14px; |
| | | color: #303133; |
| | | } |
| | | |
| | | .el-timeline-item p { |
| | | margin: 4px 0; |
| | | font-size: 12px; |
| | | color: #606266; |
| | | } |
| | | </style> |
¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <div class="app-container"> |
| | | <el-form :inline="true" :model="queryParams" class="search-form"> |
| | | <el-form-item label="éè´§åå·"> |
| | | <el-input |
| | | v-model="queryParams.returnNo" |
| | | placeholder="请è¾å
¥éè´§åå·" |
| | | clearable |
| | | :style="{ width: '200px' }" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="ä¾åºå"> |
| | | <el-select |
| | | v-model="queryParams.supplierId" |
| | | placeholder="è¯·éæ©ä¾åºå" |
| | | clearable |
| | | :style="{ width: '200px' }" |
| | | > |
| | | <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="queryParams.status" |
| | | placeholder="è¯·éæ©ç¶æ" |
| | | clearable |
| | | :style="{ width: '150px' }" |
| | | > |
| | | <el-option |
| | | :label="item.label" |
| | | v-for="item in statusList" |
| | | :key="item.value" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="åæ®æ¥æ"> |
| | | <el-date-picker |
| | | v-model="queryParams.dateRange" |
| | | type="daterange" |
| | | range-separator="è³" |
| | | start-placeholder="å¼å§æ¥æ" |
| | | end-placeholder="ç»ææ¥æ" |
| | | format="YYYY-MM-DD" |
| | | value-format="YYYY-MM-DD" |
| | | :style="{ width: '240px' }" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-button type="primary" @click="handleQuery">æ¥è¯¢</el-button> |
| | | <el-button @click="resetQuery">éç½®</el-button> |
| | | </el-form-item> |
| | | </el-form> |
| | | |
| | | <el-card> |
| | | <!-- æä½æé®åº --> |
| | | <el-row :gutter="24" class="table-toolbar" justify="space-between"> |
| | | <el-button type="primary" :icon="Plus" @click="handleAdd"> |
| | | æ°å¢éè´§å |
| | | </el-button> |
| | | <el-button type="success" :icon="Refresh" @click="handleGenerateReturn"> |
| | | ä¸é®çæéè´§å |
| | | </el-button> |
| | | <el-button type="danger" :icon="Delete" @click="handleBatchDelete" :disabled="selectedIds.length === 0"> |
| | | æ¹éå é¤ |
| | | </el-button> |
| | | </el-row> |
| | | |
| | | <!-- è¡¨æ ¼ç»ä»¶ --> |
| | | <el-table |
| | | v-loading="loading" |
| | | :data="tableData" |
| | | @selection-change="handleSelectionChange" |
| | | border |
| | | style="width: 100%" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column label="éè´§åå·" prop="returnNo" width="180" /> |
| | | <el-table-column label="ä¾åºå" prop="supplierName" width="200" /> |
| | | <el-table-column label="åæ®æ¥æ" prop="returnDate" width="120" /> |
| | | <el-table-column label="æä½å" prop="operatorName" width="120" /> |
| | | <el-table-column label="éè´§åå " prop="returnReason" width="200" show-overflow-tooltip /> |
| | | <el-table-column label="éè´§æ°é" prop="returnQuantity" width="120"> |
| | | <template #default="scope"> |
| | | {{ scope.row.returnQuantity }} å¨ |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="ç¶æ" prop="status" width="100"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getStatusType(scope.row.status)"> |
| | | {{ getStatusText(scope.row.status) }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="å建æ¶é´" prop="createTime" width="160" /> |
| | | <el-table-column label="æä½" width="200" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | size="small" |
| | | type="primary" |
| | | @click="handleView(scope.row)" |
| | | > |
| | | æ¥ç |
| | | </el-button> |
| | | <el-button |
| | | size="small" |
| | | type="warning" |
| | | @click="handleEdit(scope.row)" |
| | | v-if="scope.row.status === 'draft'" |
| | | > |
| | | ç¼è¾ |
| | | </el-button> |
| | | <el-button |
| | | size="small" |
| | | type="danger" |
| | | @click="handleDelete(scope.row)" |
| | | v-if="scope.row.status === 'draft'" |
| | | > |
| | | å é¤ |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | |
| | | <!-- å页ç»ä»¶ --> |
| | | <pagination |
| | | v-if="total > 0" |
| | | :page="current" |
| | | :limit="pageSize" |
| | | :total="total" |
| | | @pagination="handlePagination" |
| | | :layout="'total, prev, pager, next, jumper'" |
| | | /> |
| | | </el-card> |
| | | |
| | | <!-- æ°å¢/ç¼è¾å¯¹è¯æ¡ --> |
| | | <PurchaseReturnDialog |
| | | v-model:dialogFormVisible="dialogFormVisible" |
| | | v-model:form="form" |
| | | :title="title" |
| | | :is-edit="isEdit" |
| | | @submit="handleSubmit" |
| | | @success="handleSuccess" |
| | | ref="purchaseReturnDialog" |
| | | /> |
| | | |
| | | <!-- æ¥ç详æ
å¯¹è¯æ¡ --> |
| | | <PurchaseReturnViewDialog |
| | | v-model:dialogViewVisible="dialogViewVisible" |
| | | :form="viewForm" |
| | | title="éè´§å详æ
" |
| | | /> |
| | | |
| | | <!-- ä¸é®çæéè´§åå¯¹è¯æ¡ --> |
| | | <GenerateReturnDialog |
| | | v-model:dialogGenerateVisible="dialogGenerateVisible" |
| | | @success="handleGenerateSuccess" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { ref, reactive, onMounted, getCurrentInstance } from "vue"; |
| | | import { ElMessage, ElMessageBox } from "element-plus"; |
| | | import { Plus, Edit, Delete, Refresh, View } from "@element-plus/icons-vue"; |
| | | import Pagination from "@/components/Pagination"; |
| | | import PurchaseReturnDialog from "./components/PurchaseReturnDialog.vue"; |
| | | import PurchaseReturnViewDialog from "./components/PurchaseReturnViewDialog.vue"; |
| | | import GenerateReturnDialog from "./components/GenerateReturnDialog.vue"; |
| | | |
| | | // ååºå¼æ°æ® |
| | | const loading = ref(false); |
| | | const tableData = ref([]); |
| | | const selectedIds = ref([]); |
| | | const current = ref(1); |
| | | const pageSize = ref(10); |
| | | const total = ref(0); |
| | | const dialogFormVisible = ref(false); |
| | | const dialogViewVisible = ref(false); |
| | | const dialogGenerateVisible = ref(false); |
| | | const isEdit = ref(false); |
| | | const title = ref(""); |
| | | const form = ref({}); |
| | | const viewForm = ref({}); |
| | | |
| | | // æ¥è¯¢åæ° |
| | | const queryParams = reactive({ |
| | | returnNo: "", |
| | | supplierId: "", |
| | | status: "", |
| | | dateRange: [] |
| | | }); |
| | | |
| | | // ä¾åºåå表 |
| | | const supplierList = ref([ |
| | | { value: "1", label: "ä¾åºåA" }, |
| | | { value: "2", label: "ä¾åºåB" }, |
| | | { value: "3", label: "ä¾åºåC" } |
| | | ]); |
| | | |
| | | // ç¶æå表 |
| | | const statusList = ref([ |
| | | { value: "draft", label: "è稿" }, |
| | | { value: "pending", label: "å¾
å®¡æ ¸" }, |
| | | { value: "approved", label: "å·²å®¡æ ¸" }, |
| | | { value: "rejected", label: "å·²æç»" }, |
| | | { value: "completed", label: "已宿" } |
| | | ]); |
| | | |
| | | // æ¨¡ææ°æ® |
| | | const mockData = [ |
| | | { |
| | | id: "1", |
| | | returnNo: "TH20241201001", |
| | | supplierName: "ä¾åºåA", |
| | | returnDate: "2024-12-01", |
| | | operatorName: "éå¿å¼º", |
| | | returnReason: "è´¨éä¸åæ ¼ï¼ç
¤è´¨ä¸ç¬¦åè¦æ±", |
| | | returnQuantity: 50, |
| | | status: "pending", |
| | | createTime: "2024-12-01 10:00:00" |
| | | }, |
| | | { |
| | | id: "2", |
| | | returnNo: "TH20241201002", |
| | | supplierName: "ä¾åºåB", |
| | | returnDate: "2024-12-01", |
| | | operatorName: "åç¾ç²", |
| | | returnReason: "交货æ»åï¼å½±åç产计å", |
| | | returnQuantity: 30, |
| | | status: "approved", |
| | | createTime: "2024-12-01 14:30:00" |
| | | } |
| | | ]; |
| | | |
| | | // è·åç¶æç±»å |
| | | const getStatusType = (status) => { |
| | | const statusMap = { |
| | | draft: "", |
| | | pending: "warning", |
| | | approved: "success", |
| | | rejected: "danger", |
| | | completed: "info" |
| | | }; |
| | | return statusMap[status] || ""; |
| | | }; |
| | | |
| | | // è·åç¶æææ¬ |
| | | const getStatusText = (status) => { |
| | | const statusMap = { |
| | | draft: "è稿", |
| | | pending: "å¾
å®¡æ ¸", |
| | | approved: "å·²å®¡æ ¸", |
| | | rejected: "å·²æç»", |
| | | completed: "已宿" |
| | | }; |
| | | return statusMap[status] || status; |
| | | }; |
| | | |
| | | // æ¥è¯¢ |
| | | const handleQuery = () => { |
| | | current.value = 1; |
| | | loadData(); |
| | | }; |
| | | |
| | | // éç½®æ¥è¯¢ |
| | | const resetQuery = () => { |
| | | Object.assign(queryParams, { |
| | | returnNo: "", |
| | | supplierId: "", |
| | | status: "", |
| | | dateRange: [] |
| | | }); |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // å è½½æ°æ® |
| | | const loadData = () => { |
| | | loading.value = true; |
| | | // 模æAPIè°ç¨ |
| | | setTimeout(() => { |
| | | tableData.value = mockData; |
| | | total.value = mockData.length; |
| | | loading.value = false; |
| | | }, 500); |
| | | }; |
| | | |
| | | // å页å¤ç |
| | | const handlePagination = (pagination) => { |
| | | current.value = pagination.page; |
| | | pageSize.value = pagination.limit; |
| | | loadData(); |
| | | }; |
| | | |
| | | // éæ©åå |
| | | const handleSelectionChange = (selection) => { |
| | | selectedIds.value = selection.map(item => item.id); |
| | | }; |
| | | |
| | | // æ°å¢ |
| | | const handleAdd = () => { |
| | | isEdit.value = false; |
| | | title.value = "æ°å¢éè´§å"; |
| | | form.value = { |
| | | supplierId: "", |
| | | returnDate: "", |
| | | operatorId: "", |
| | | returnReason: "", |
| | | returnQuantity: "", |
| | | returnAmount: "", |
| | | returnItems: [], |
| | | remark: "" |
| | | }; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | |
| | | // ç¼è¾ |
| | | const handleEdit = (row) => { |
| | | isEdit.value = true; |
| | | title.value = "ç¼è¾éè´§å"; |
| | | form.value = { ...row }; |
| | | dialogFormVisible.value = true; |
| | | }; |
| | | |
| | | // æ¥ç |
| | | const handleView = (row) => { |
| | | viewForm.value = { ...row }; |
| | | dialogViewVisible.value = true; |
| | | }; |
| | | |
| | | // å é¤ |
| | | const handleDelete = (row) => { |
| | | ElMessageBox.confirm( |
| | | `ç¡®å®è¦å é¤éè´§å ${row.returnNo} åï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | // 模æå é¤ |
| | | const index = tableData.value.findIndex(item => item.id === row.id); |
| | | if (index > -1) { |
| | | tableData.value.splice(index, 1); |
| | | total.value--; |
| | | ElMessage.success("å 餿å"); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // æ¹éå é¤ |
| | | const handleBatchDelete = () => { |
| | | if (selectedIds.value.length === 0) { |
| | | ElMessage.warning("è¯·éæ©è¦å é¤çè®°å½"); |
| | | return; |
| | | } |
| | | |
| | | ElMessageBox.confirm( |
| | | `ç¡®å®è¦å é¤éä¸ç ${selectedIds.value.length} æ¡è®°å½åï¼`, |
| | | "æç¤º", |
| | | { |
| | | confirmButtonText: "ç¡®å®", |
| | | cancelButtonText: "åæ¶", |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | // æ¨¡ææ¹éå é¤ |
| | | tableData.value = tableData.value.filter(item => !selectedIds.value.includes(item.id)); |
| | | total.value = tableData.value.length; |
| | | selectedIds.value = []; |
| | | ElMessage.success("æ¹éå 餿å"); |
| | | }); |
| | | }; |
| | | |
| | | // ä¸é®çæéè´§å |
| | | const handleGenerateReturn = () => { |
| | | dialogGenerateVisible.value = true; |
| | | }; |
| | | |
| | | // æäº¤è¡¨å |
| | | const handleSubmit = (formData) => { |
| | | if (isEdit.value) { |
| | | // ç¼è¾ |
| | | const index = tableData.value.findIndex(item => item.id === formData.id); |
| | | if (index > -1) { |
| | | tableData.value[index] = { ...formData }; |
| | | ElMessage.success("ç¼è¾æå"); |
| | | } |
| | | } else { |
| | | // æ°å¢ |
| | | const newItem = { |
| | | id: Date.now().toString(), |
| | | returnNo: `TH${Date.now()}`, |
| | | supplierName: supplierList.value.find(item => item.value === formData.supplierId)?.label || "", |
| | | returnDate: formData.returnDate, |
| | | operatorName: "å½åç¨æ·", |
| | | returnReason: formData.returnReason, |
| | | returnQuantity: formData.returnQuantity, |
| | | status: "draft", |
| | | createTime: new Date().toLocaleString() |
| | | }; |
| | | tableData.value.unshift(newItem); |
| | | total.value++; |
| | | ElMessage.success("æ°å¢æå"); |
| | | } |
| | | dialogFormVisible.value = false; |
| | | }; |
| | | |
| | | // 表åæååè° |
| | | const handleSuccess = () => { |
| | | loadData(); |
| | | }; |
| | | |
| | | // çæéè´§åæååè° |
| | | const handleGenerateSuccess = (returnOrder) => { |
| | | dialogGenerateVisible.value = false; |
| | | // å°çæçéè´§åæ·»å å°åè¡¨ä¸ |
| | | if (returnOrder) { |
| | | const newItem = { |
| | | id: Date.now().toString(), |
| | | returnNo: returnOrder.returnNo, |
| | | supplierName: returnOrder.supplierName, |
| | | returnDate: returnOrder.returnDate, |
| | | operatorName: returnOrder.operatorName, |
| | | returnReason: returnOrder.returnReason, |
| | | returnQuantity: returnOrder.returnQuantity, |
| | | status: returnOrder.status, |
| | | createTime: returnOrder.createTime, |
| | | returnItems: returnOrder.returnItems |
| | | }; |
| | | tableData.value.unshift(newItem); |
| | | total.value++; |
| | | } |
| | | loadData(); |
| | | ElMessage.success("éè´§åçææå"); |
| | | }; |
| | | |
| | | // 页é¢å è½½ |
| | | onMounted(() => { |
| | | loadData(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .search-form { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .table-toolbar { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .el-card { |
| | | margin-bottom: 20px; |
| | | } |
| | | </style> |