3.1
<template>
<div class="procurement-page">
<div class="page-header">
<h2>备件采购</h2>
<div class="header-actions">
<el-button type="primary" @click="showAddDialog = true">新增采购申请</el-button>
<el-button type="info" @click="showStatistics">统计信息</el-button>
<el-button type="success" @click="batchUpdateStatus">批量更新状态</el-button>
</div>
</div>
<div class="search-bar">
<el-row :gutter="20">
<el-col :span="6">
<el-input
v-model="searchKeyword"
placeholder="搜索备件名称或型号"
@keyup.enter="searchRecords"
/>
</el-col>
<el-col :span="4">
<el-select v-model="filterApplicant" placeholder="申请人" clearable>
<el-option
v-for="applicant in applicantOptions"
:key="applicant"
:label="applicant"
:value="applicant"
/>
</el-select>
</el-col>
<el-col :span="4">
<el-select v-model="filterWorkArea" placeholder="工区" clearable>
<el-option
v-for="area in workAreaOptions"
:key="area"
:label="area"
:value="area"
/>
</el-select>
</el-col>
<el-col :span="6">
<el-button type="primary" @click="searchRecords">搜索</el-button>
<el-button @click="resetSearch">重置</el-button>
<el-button type="warning" @click="exportData">导出</el-button>
</el-col>
</el-row>
</div>
<div class="status-tabs">
<el-tabs v-model="activeStatus" @tab-click="handleStatusChange">
<el-tab-pane label="全部" name="all"></el-tab-pane>
<el-tab-pane label="待上会" name="待上会"></el-tab-pane>
<el-tab-pane label="待招标" name="待招标"></el-tab-pane>
<el-tab-pane label="采购中" name="采购中"></el-tab-pane>
<el-tab-pane label="待发货" name="待发货"></el-tab-pane>
<el-tab-pane label="待收货" name="待收货"></el-tab-pane>
<el-tab-pane label="已完成" name="已完成"></el-tab-pane>
</el-tabs>
</div>
<el-table
:data="procurementRecords"
style="width: 100%"
v-loading="loading"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55"></el-table-column>
<el-table-column prop="id" label="ID" width="80"></el-table-column>
<el-table-column prop="applicant" label="申请人" width="120"></el-table-column>
<el-table-column prop="requiredStation" label="需求车站" width="120"></el-table-column>
<el-table-column prop="workArea" label="所属工区" width="120"></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="quantity" label="数量" width="80"></el-table-column>
<el-table-column prop="currentStock" label="当前库存" width="100">
<template slot-scope="scope">
<span :class="{ 'low-stock': scope.row.currentStock < 10 }">
{{ scope.row.currentStock || 0 }}
</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.status)">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="createdAt" label="申请时间" width="150">
<template slot-scope="scope">
{{ formatDate(scope.row.createdAt) }}
</template>
</el-table-column>
<el-table-column label="操作" width="250">
<template slot-scope="scope">
<el-button size="mini" type="info" @click="viewRecord(scope.row)">详情</el-button>
<el-button size="mini" type="primary" @click="editRecord(scope.row)">编辑</el-button>
<el-button size="mini" type="success" @click="updateStatus(scope.row)">更新状态</el-button>
<el-button size="mini" type="warning" @click="viewStock(scope.row)">查看库存</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<div class="pagination">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
<!-- 新增/编辑对话框 -->
<el-dialog :title="dialogTitle" :visible.sync="showAddDialog" width="600px">
<el-form :model="currentRecord" :rules="rules" ref="recordForm" label-width="120px">
<el-form-item label="申请人" prop="applicant">
<el-select v-model="currentRecord.applicant" placeholder="请选择申请人" filterable allow-create>
<el-option
v-for="applicant in applicantOptions"
:key="applicant"
:label="applicant"
:value="applicant"
/>
</el-select>
</el-form-item>
<el-form-item label="需求车站" prop="requiredStation">
<el-input v-model="currentRecord.requiredStation"></el-input>
</el-form-item>
<el-form-item label="所属工区" prop="workArea">
<el-select v-model="currentRecord.workArea" placeholder="请选择工区" filterable allow-create>
<el-option
v-for="area in workAreaOptions"
:key="area"
:label="area"
:value="area"
/>
</el-select>
</el-form-item>
<el-form-item label="备件名称" prop="partName">
<el-select v-model="currentRecord.partName" placeholder="请选择备件名称" filterable allow-create @change="onPartNameChange">
<el-option
v-for="name in sparePartNames"
:key="name"
:label="name"
:value="name"
/>
</el-select>
</el-form-item>
<el-form-item label="备件型号" prop="partModel">
<el-select v-model="currentRecord.partModel" placeholder="请选择备件型号" filterable allow-create @change="checkCurrentStock">
<el-option
v-for="model in sparePartModels"
:key="model"
:label="model"
:value="model"
/>
</el-select>
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input-number v-model="currentRecord.quantity" :min="1"></el-input-number>
</el-form-item>
<el-form-item label="当前库存">
<el-input v-model="currentStock" disabled></el-input>
</el-form-item>
<el-form-item label="申请原因" prop="reason">
<el-input type="textarea" v-model="currentRecord.reason" rows="3"></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="saveRecord">确定</el-button>
</div>
</el-dialog>
<!-- 统计信息对话框 -->
<el-dialog title="采购统计信息" :visible.sync="showStatisticsDialog" width="500px">
<div class="statistics-content">
<div v-for="(count, status) in statistics" :key="status" class="stat-item">
<span class="stat-label">{{ status }}:</span>
<span class="stat-value">{{ count }}</span>
</div>
</div>
</el-dialog>
<!-- 批量操作对话框 -->
<el-dialog title="批量更新状态" :visible.sync="showBatchDialog" width="400px">
<el-form>
<el-form-item label="目标状态">
<el-select v-model="batchStatus" placeholder="请选择状态">
<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-form>
<div slot="footer">
<el-button @click="showBatchDialog = false">取消</el-button>
<el-button type="primary" @click="confirmBatchUpdate">确定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'ProcurementPage',
data() {
return {
procurementRecords: [],
loading: false,
searchKeyword: '',
filterApplicant: '',
filterWorkArea: '',
activeStatus: 'all',
showAddDialog: false,
showStatisticsDialog: false,
showBatchDialog: false,
currentRecord: {
applicant: '',
requiredStation: '',
workArea: '',
partName: '',
partModel: '',
quantity: 1,
reason: ''
},
currentStock: '',
selectedRecords: [],
batchStatus: '',
statistics: {},
applicantOptions: [],
workAreaOptions: ['工区一', '工区二', '工区三', '工区四'],
sparePartNames: [],
sparePartModels: [],
currentPage: 1,
pageSize: 20,
total: 0,
rules: {
applicant: [{ required: true, message: '请输入申请人', trigger: 'blur' }],
partName: [{ required: true, message: '请输入备件名称', trigger: 'blur' }],
quantity: [{ required: true, message: '请输入数量', trigger: 'blur' }]
}
}
},
computed: {
dialogTitle() {
return this.currentRecord.id ? '编辑采购申请' : '新增采购申请'
}
},
mounted() {
this.loadRecords()
this.loadApplicantOptions()
this.loadSparePartNames()
},
methods: {
async loadRecords() {
this.loading = true
try {
const response = await axios.get('/procurement')
this.procurementRecords = response.data
this.total = response.data.length
} catch (error) {
this.$message.error('加载数据失败')
console.error(error)
} finally {
this.loading = false
}
},
async loadApplicantOptions() {
try {
const response = await axios.get('/operators/applicants')
this.applicantOptions = response.data
} catch (error) {
console.error('加载申请人选项失败', error)
}
},
async loadSparePartNames() {
try {
const response = await axios.get('/spare-parts/names')
this.sparePartNames = response.data
} catch (error) {
console.error('加载备件名称失败', error)
}
},
async onPartNameChange(partName) {
if (partName) {
try {
const response = await axios.get('/spare-parts/models', { params: { partName } })
this.sparePartModels = response.data
} catch (error) {
console.error('加载备件型号失败', error)
}
} else {
this.sparePartModels = []
}
this.currentRecord.partModel = ''
this.currentStock = ''
},
async checkCurrentStock() {
if (this.currentRecord.partName && this.currentRecord.partModel) {
try {
const response = await axios.get('/procurement/stock', {
params: {
partName: this.currentRecord.partName,
partModel: this.currentRecord.partModel
}
})
this.currentStock = response.data
} catch (error) {
console.error('查询库存失败', error)
this.currentStock = '查询失败'
}
}
},
async searchRecords() {
if (!this.searchKeyword.trim() && !this.filterApplicant && !this.filterWorkArea) {
this.loadRecords()
return
}
this.loading = true
try {
let url = '/procurement'
if (this.searchKeyword.trim()) {
url += `/search?keyword=${encodeURIComponent(this.searchKeyword)}`
} else {
if (this.filterApplicant) {
url += `/applicant/${encodeURIComponent(this.filterApplicant)}`
} else if (this.filterWorkArea) {
url += `/work-area/${encodeURIComponent(this.filterWorkArea)}`
}
}
const response = await axios.get(url)
this.procurementRecords = response.data
this.total = response.data.length
} catch (error) {
this.$message.error('搜索失败')
console.error(error)
} finally {
this.loading = false
}
},
resetSearch() {
this.searchKeyword = ''
this.filterApplicant = ''
this.filterWorkArea = ''
this.activeStatus = 'all'
this.loadRecords()
},
async handleStatusChange(tab) {
if (tab.name === 'all') {
this.loadRecords()
} else {
this.loading = true
try {
const response = await axios.get(`/procurement/status/${tab.name}`)
this.procurementRecords = response.data
this.total = response.data.length
} catch (error) {
this.$message.error('加载数据失败')
} finally {
this.loading = false
}
}
},
viewRecord(record) {
this.$alert(JSON.stringify(record, null, 2), '采购详情', {
confirmButtonText: '确定'
})
},
editRecord(record) {
this.currentRecord = { ...record }
this.showAddDialog = true
if (record.partName) {
this.onPartNameChange(record.partName)
}
},
async saveRecord() {
this.$refs.recordForm.validate(async (valid) => {
if (valid) {
try {
if (this.currentRecord.id) {
await axios.put(`/procurement/${this.currentRecord.id}`, this.currentRecord)
this.$message.success('更新成功')
} else {
await axios.post('/procurement', this.currentRecord)
this.$message.success('创建成功')
}
this.showAddDialog = false
this.loadRecords()
this.loadApplicantOptions()
} catch (error) {
this.$message.error('保存失败')
}
}
})
},
async updateStatus(record) {
const statusOptions = ['待招标', '采购中', '待发货', '待收货', '已完成']
this.$prompt('请选择新状态', '更新状态', {
inputType: 'select',
inputOptions: statusOptions.map(s => ({ value: s, label: s })),
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(async ({ value }) => {
try {
await axios.put(`/procurement/${record.id}/status?status=${value}`)
this.$message.success('状态更新成功')
this.loadRecords()
} catch (error) {
this.$message.error('状态更新失败')
}
})
},
viewStock(record) {
this.$message.info(`${record.partName} ${record.partModel} 当前库存: ${record.currentStock || 0}`)
},
async showStatistics() {
try {
const response = await axios.get('/procurement/statistics')
this.statistics = response.data
this.showStatisticsDialog = true
} catch (error) {
this.$message.error('加载统计信息失败')
}
},
batchUpdateStatus() {
if (this.selectedRecords.length === 0) {
this.$message.warning('请选择要更新的记录')
return
}
this.showBatchDialog = true
},
async confirmBatchUpdate() {
if (!this.batchStatus) {
this.$message.warning('请选择目标状态')
return
}
try {
const ids = this.selectedRecords.map(r => r.id)
await axios.put('/procurement/batch-status', {
ids: ids,
status: this.batchStatus
})
this.$message.success('批量更新成功')
this.showBatchDialog = false
this.loadRecords()
} catch (error) {
this.$message.error('批量更新失败')
}
},
exportData() {
this.$message.info('导出功能开发中...')
},
handleSelectionChange(selection) {
this.selectedRecords = selection
},
handleSizeChange(val) {
this.pageSize = val
this.loadRecords()
},
handleCurrentChange(val) {
this.currentPage = val
this.loadRecords()
},
getStatusType(status) {
const statusMap = {
'待上会': 'info',
'待招标': 'warning',
'采购中': 'primary',
'待发货': 'success',
'待收货': 'warning',
'已完成': 'success'
}
return statusMap[status] || 'info'
},
formatDate(date) {
if (!date) return ''
return new Date(date).toLocaleString()
}
}
}
</script>
<style scoped>
.procurement-page {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.search-bar {
margin-bottom: 20px;
}
.status-tabs {
margin-bottom: 20px;
}
.pagination {
margin-top: 20px;
text-align: right;
}
.statistics-content {
padding: 20px;
}
.stat-item {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.stat-label {
font-weight: bold;
}
.stat-value {
color: #409eff;
}
.low-stock {
color: #f56c6c;
font-weight: bold;
}
</style>
浙公网安备 33010602011771号