2.26
<template>
<div class="inventory-page">
<div class="page-header">
<h2>库存管理</h2>
<div>
<el-button type="primary" @click="showAddDialog = true">新增备件</el-button>
<el-button type="warning" @click="showLowStock">库存预警</el-button>
</div>
</div>
<div class="search-bar">
<el-input
v-model="searchKeyword"
placeholder="搜索备件名称或型号"
style="width: 300px; margin-right: 10px;"
@keyup.enter="searchParts"
/>
<el-select v-model="selectedStatus" placeholder="选择状态" style="width: 150px; margin-right: 10px;">
<el-option label="全部状态" value=""></el-option>
<el-option label="新好件" value="新好件"></el-option>
<el-option label="修好件" value="修好件"></el-option>
<el-option label="坏件" value="坏件"></el-option>
<el-option label="二级修" value="二级修"></el-option>
<el-option label="返厂修" value="返厂修"></el-option>
<el-option label="待调拨" value="待调拨"></el-option>
<el-option label="待报废" value="待报废"></el-option>
<el-option label="已报废" value="已报废"></el-option>
</el-select>
<el-button type="primary" @click="searchParts">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
</div>
<el-table :data="spareParts" style="width: 100%" v-loading="loading">
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="partName" label="备件名称" width="150"></el-table-column>
<el-table-column prop="partModel" label="备件型号" width="120"></el-table-column>
<el-table-column prop="partCategory" label="分类" width="100"></el-table-column>
<el-table-column prop="partStatus" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.partStatus)">{{ scope.row.partStatus }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="partType" label="类型" width="100"></el-table-column>
<el-table-column prop="quantity" label="当前库存" width="100">
<template slot-scope="scope">
<span :class="{ 'low-stock': scope.row.quantity <= scope.row.minStock }">
{{ scope.row.quantity }}
</span>
</template>
</el-table-column>
<el-table-column prop="minStock" label="最小库存" width="100"></el-table-column>
<el-table-column prop="maxStock" label="最大库存" width="100"></el-table-column>
<el-table-column prop="location" label="库位" width="120"></el-table-column>
<el-table-column prop="manufacturer" label="生产厂家" width="120"></el-table-column>
<el-table-column prop="unitPrice" label="单价" width="100">
<template slot-scope="scope">
¥{{ scope.row.unitPrice }}
</template>
</el-table-column>
<el-table-column label="操作" width="250">
<template slot-scope="scope">
<el-button size="mini" @click="viewPart(scope.row)">查看</el-button>
<el-button size="mini" type="primary" @click="editPart(scope.row)">编辑</el-button>
<el-button size="mini" type="warning" @click="adjustStock(scope.row)">调整库存</el-button>
<el-button size="mini" type="danger" @click="deletePart(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 新增/编辑对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="showAddDialog" width="800px">
<el-form :model="currentPart" :rules="rules" ref="partForm" label-width="120px">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="备件名称" prop="partName">
<el-input v-model="currentPart.partName"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备件型号" prop="partModel">
<el-input v-model="currentPart.partModel"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="备件分类" prop="partCategory">
<el-input v-model="currentPart.partCategory"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="备件状态" prop="partStatus">
<el-select v-model="currentPart.partStatus" style="width: 100%">
<el-option label="新好件" value="新好件"></el-option>
<el-option label="修好件" value="修好件"></el-option>
<el-option label="坏件" value="坏件"></el-option>
<el-option label="二级修" value="二级修"></el-option>
<el-option label="返厂修" value="返厂修"></el-option>
<el-option label="待调拨" value="待调拨"></el-option>
<el-option label="待报废" value="待报废"></el-option>
<el-option label="已报废" value="已报废"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="备件类型" prop="partType">
<el-select v-model="currentPart.partType" style="width: 100%">
<el-option label="正常件" value="正常件"></el-option>
<el-option label="在保件" value="在保件"></el-option>
<el-option label="遗留件" value="遗留件"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单价" prop="unitPrice">
<el-input-number v-model="currentPart.unitPrice" :precision="2" :min="0"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="数量" prop="quantity">
<el-input-number v-model="currentPart.quantity" :min="0"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最小库存" prop="minStock">
<el-input-number v-model="currentPart.minStock" :min="0"></el-input-number>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="最大库存" prop="maxStock">
<el-input-number v-model="currentPart.maxStock" :min="0"></el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="库位" prop="location">
<el-input v-model="currentPart.location"></el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位" prop="unit">
<el-input v-model="currentPart.unit"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="生产厂家" prop="manufacturer">
<el-input v-model="currentPart.manufacturer"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showAddDialog = false">取消</el-button>
<el-button type="primary" @click="savePart">确定</el-button>
</div>
</el-dialog>
<!-- 库存调整对话框 -->
<el-dialog title="库存调整" :visible.sync="showStockDialog" width="400px">
<el-form :model="stockAdjustment" label-width="100px">
<el-form-item label="备件名称">
<span>{{ stockAdjustment.partName }}</span>
</el-form-item>
<el-form-item label="当前库存">
<span>{{ stockAdjustment.currentQuantity }}</span>
</el-form-item>
<el-form-item label="新库存" prop="newQuantity">
<el-input-number v-model="stockAdjustment.newQuantity" :min="0"></el-input-number>
</el-form-item>
<el-form-item label="调整原因">
<el-input type="textarea" v-model="stockAdjustment.reason" rows="3"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="showStockDialog = false">取消</el-button>
<el-button type="primary" @click="saveStockAdjustment">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'InventoryPage',
data() {
return {
spareParts: [],
loading: false,
searchKeyword: '',
selectedStatus: '',
showAddDialog: false,
showStockDialog: false,
currentPart: this.getEmptyPart(),
stockAdjustment: {
id: null,
partName: '',
currentQuantity: 0,
newQuantity: 0,
reason: ''
},
rules: {
partName: [{ required: true, message: '请输入备件名称', trigger: 'blur' }],
partModel: [{ required: true, message: '请输入备件型号', trigger: 'blur' }],
partStatus: [{ required: true, message: '请选择备件状态', trigger: 'change' }],
partType: [{ required: true, message: '请选择备件类型', trigger: 'change' }],
quantity: [{ required: true, message: '请输入数量', trigger: 'blur' }]
}
}
},
computed: {
dialogTitle() {
return this.currentPart.id ? '编辑备件' : '新增备件'
}
},
mounted() {
this.loadParts()
},
methods: {
getEmptyPart() {
return {
partName: '',
partModel: '',
partCategory: '',
partStatus: '新好件',
partType: '正常件',
unitPrice: 0,
quantity: 0,
minStock: 0,
maxStock: 0,
location: '',
unit: '',
manufacturer: ''
}
},
async loadParts() {
this.loading = true
try {
const response = await this.$http.get('/spare-parts')
this.spareParts = response.data
} catch (error) {
this.$message.error('加载数据失败')
} finally {
this.loading = false
}
},
async searchParts() {
this.loading = true
try {
let url = '/spare-parts'
if (this.searchKeyword.trim()) {
url = `/spare-parts/search?keyword=${this.searchKeyword}`
} else if (this.selectedStatus) {
url = `/spare-parts/status/${this.selectedStatus}`
}
const response = await this.$http.get(url)
this.spareParts = response.data
} catch (error) {
this.$message.error('搜索失败')
} finally {
this.loading = false
}
},
resetSearch() {
this.searchKeyword = ''
this.selectedStatus = ''
this.loadParts()
},
async showLowStock() {
this.loading = true
try {
const response = await this.$http.get('/spare-parts/low-stock')
this.spareParts = response.data
this.$message.info(`找到 ${response.data.length} 个库存不足的备件`)
} catch (error) {
this.$message.error('加载库存预警数据失败')
} finally {
this.loading = false
}
},
viewPart(part) {
this.$message.info('查看功能待实现')
},
editPart(part) {
this.currentPart = { ...part }
this.showAddDialog = true
},
adjustStock(part) {
this.stockAdjustment = {
id: part.id,
partName: part.partName,
currentQuantity: part.quantity,
newQuantity: part.quantity,
reason: ''
}
this.showStockDialog = true
},
async deletePart(part) {
this.$confirm('确认删除该备件?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
await this.$http.delete(`/spare-parts/${part.id}`)
this.$message.success('删除成功')
this.loadParts()
} catch (error) {
this.$message.error('删除失败')
}
})
},
async savePart() {
this.$refs.partForm.validate(async (valid) => {
if (valid) {
try {
if (this.currentPart.id) {
await this.$http.put(`/spare-parts/${this.currentPart.id}`, this.currentPart)
this.$message.success('更新成功')
} else {
await this.$http.post('/spare-parts', this.currentPart)
this.$message.success('创建成功')
}
this.showAddDialog = false
this.currentPart = this.getEmptyPart()
this.loadParts()
} catch (error) {
this.$message.error('保存失败')
}
}
})
},
async saveStockAdjustment() {
try {
await this.$http.put(`/spare-parts/${this.stockAdjustment.id}/quantity?quantity=${this.stockAdjustment.newQuantity}`)
this.$message.success('库存调整成功')
this.showStockDialog = false
this.loadParts()
} catch (error) {
this.$message.error('库存调整失败')
}
},
getStatusType(status) {
const statusMap = {
'新好件': 'success',
'修好件': 'success',
'坏件': 'danger',
'二级修': 'warning',
'返厂修': 'warning',
'待调拨': 'info',
'待报废': 'danger',
'已报废': 'danger'
}
return statusMap[status] || 'info'
}
}
}
</script>
<style scoped>
.inventory-page {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.search-bar {
margin-bottom: 20px;
}
.low-stock {
color: #f56c6c;
font-weight: bold;
}
</style>