398 lines
14 KiB
Vue
398 lines
14 KiB
Vue
<template>
|
||
<el-container>
|
||
<el-main class="nopadding">
|
||
<div style="padding: 20px;">
|
||
<el-descriptions title="物资领用单" :column="3" border>
|
||
<el-descriptions-item label="编号">{{ formData.number || '自动生成' }}</el-descriptions-item>
|
||
<el-descriptions-item label="领用部门">{{ formData.belong_dept_name || currentUser.belong_dept_name }}</el-descriptions-item>
|
||
<el-descriptions-item label="填报时间">
|
||
<el-date-picker
|
||
v-if="isEditable"
|
||
v-model="formData.req_date"
|
||
type="date"
|
||
placeholder="填报时间"
|
||
value-format="YYYY-MM-DD"
|
||
style="width: 200px;"
|
||
/>
|
||
<span v-else>{{ formData.req_date }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="操作人">{{ formData.create_by_name || currentUser.name }}</el-descriptions-item>
|
||
<el-descriptions-item label="领取人">
|
||
<el-input v-if="isEditable" v-model="formData.collector" placeholder="领取人" style="width: 200px;" />
|
||
<span v-else>{{ formData.collector }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="备注">
|
||
<el-input v-if="isEditable" v-model="formData.note" type="textarea" :rows="1" placeholder="备注" />
|
||
<span v-else>{{ formData.note }}</span>
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<!-- 库存物品(从物料库存中选择) -->
|
||
<div style="margin-top: 20px;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||
<h4 style="margin: 0;">库存物品领用</h4>
|
||
<el-button v-if="isEditable" type="primary" size="small" icon="el-icon-plus" @click="openStockPicker">从库存选择</el-button>
|
||
</div>
|
||
<el-table :data="stockItems" border stripe style="width: 100%">
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column label="领用类型" width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.req_type" placeholder="领用类型" />
|
||
<span v-else>{{ scope.row.req_type }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="物资名称" min-width="120" prop="name" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="规格型号" width="120" prop="spec" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="单位" width="80" prop="unit"></el-table-column>
|
||
<el-table-column label="库存量" width="100">
|
||
<template #default="scope">
|
||
<span style="color: #909399;">{{ scope.row.stock_quantity }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="领用量" width="130">
|
||
<template #default="scope">
|
||
<el-input-number
|
||
v-if="isEditable"
|
||
v-model="scope.row.quantity"
|
||
:min="0"
|
||
:max="Number(scope.row.stock_quantity)"
|
||
:precision="3"
|
||
size="small"
|
||
controls-position="right"
|
||
/>
|
||
<span v-else>{{ scope.row.quantity }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="备注" min-width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.note" placeholder="备注" />
|
||
<span v-else>{{ scope.row.note }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column v-if="isEditable" label="操作" width="80" align="center" fixed="right">
|
||
<template #default="scope">
|
||
<el-button link type="danger" @click="delStockItem(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<!-- 非物资清单物品 -->
|
||
<div style="margin-top: 20px;">
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
||
<h4 style="margin: 0;">非物资清单物品</h4>
|
||
<el-button v-if="isEditable" type="primary" size="small" icon="el-icon-plus" @click="addNonStockItem">添加</el-button>
|
||
</div>
|
||
<el-table :data="nonStockItems" border stripe style="width: 100%">
|
||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||
<el-table-column label="领用类型" width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.req_type" placeholder="领用类型" />
|
||
<span v-else>{{ scope.row.req_type }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="物资名称" min-width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.name" placeholder="名称" />
|
||
<span v-else>{{ scope.row.name }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="规格型号" width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.spec" placeholder="规格型号" />
|
||
<span v-else>{{ scope.row.spec }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="单位" width="80">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.unit" placeholder="单位" />
|
||
<span v-else>{{ scope.row.unit }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="领用量" width="130">
|
||
<template #default="scope">
|
||
<el-input-number v-if="isEditable" v-model="scope.row.quantity" :min="0" :precision="3" size="small" controls-position="right" />
|
||
<span v-else>{{ scope.row.quantity }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="备注" min-width="120">
|
||
<template #default="scope">
|
||
<el-input v-if="isEditable" v-model="scope.row.note" placeholder="备注" />
|
||
<span v-else>{{ scope.row.note }}</span>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column v-if="isEditable" label="操作" width="80" align="center" fixed="right">
|
||
<template #default="scope">
|
||
<el-button link type="danger" @click="delNonStockItem(scope.$index)">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</div>
|
||
|
||
<div style="text-align: right; margin-top: 15px; font-size: 14px;">
|
||
<span style="margin-right: 30px;">部门负责人:___________</span>
|
||
<span>领取人:{{ formData.collector || '___________' }}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<el-footer style="padding: 10px 20px; text-align: left;">
|
||
<el-button type="danger"
|
||
v-if="localMode === 'edit'"
|
||
style="margin-right: 4px;"
|
||
@click="handleDel"
|
||
:loading="saveLoading"
|
||
>删除</el-button>
|
||
<el-button
|
||
v-if="isEditable"
|
||
type="primary"
|
||
style="margin-right: 4px;"
|
||
@click="handleSubmit"
|
||
:loading="saveLoading"
|
||
>提交审批</el-button>
|
||
<ticketd_b
|
||
v-if="formData.ticket_ && localMode === 'show'"
|
||
:t_id="formData.id"
|
||
:ticket_="formData.ticket_"
|
||
:ticket_data="ticket_data"
|
||
@success="$emit('success')"
|
||
ref="ticketd_b"
|
||
></ticketd_b>
|
||
</el-footer>
|
||
</el-main>
|
||
<el-aside width="20%" v-if="formData.ticket_">
|
||
<ticketd :ticket_="formData.ticket_" @success="$emit('success')"></ticketd>
|
||
</el-aside>
|
||
</el-container>
|
||
|
||
<!-- 库存选择对话框 -->
|
||
<el-dialog title="选择库存物品" v-model="stockPickerVisible" width="80%" destroy-on-close>
|
||
<div style="margin-bottom: 10px; display: flex; gap: 10px;">
|
||
<el-input v-model="stockSearch" placeholder="搜索名称/规格/供应商" clearable style="width: 300px;" @keyup.enter="loadStockList"></el-input>
|
||
<el-button type="primary" @click="loadStockList">搜索</el-button>
|
||
</div>
|
||
<el-table :data="stockList" border stripe @selection-change="handleStockSelectionChange" max-height="400">
|
||
<el-table-column type="selection" width="55" />
|
||
<el-table-column label="名称" prop="name" min-width="120" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="规格" prop="spec" width="120" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="单位" prop="unit" width="80"></el-table-column>
|
||
<el-table-column label="库存量" prop="quantity" width="100"></el-table-column>
|
||
<el-table-column label="仓库" prop="warehouse_name" width="100" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="入库单号" prop="entry_number" width="180" show-overflow-tooltip></el-table-column>
|
||
<el-table-column label="供应商" prop="supplier_name" width="120" show-overflow-tooltip></el-table-column>
|
||
</el-table>
|
||
<template #footer>
|
||
<el-button @click="stockPickerVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="confirmStockPick">确认选择 ({{ selectedStocks.length }})</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script>
|
||
import ticketd_b from "@/views/wf/ticketd_b.vue"
|
||
import ticketd from '@/views/wf/ticketd.vue'
|
||
|
||
export default {
|
||
name: 'MaterialRequisitionForm',
|
||
components: { ticketd_b, ticketd },
|
||
props: {
|
||
mode: { type: String, default: 'show' },
|
||
t_id: { type: String, default: "" }
|
||
},
|
||
emits: ['success'],
|
||
data() {
|
||
return {
|
||
formData: {},
|
||
stockItems: [],
|
||
nonStockItems: [],
|
||
localMode: this.mode,
|
||
saveLoading: false,
|
||
ticket_data: {},
|
||
currentUser: this.$TOOL.data.get("USER_INFO") || {},
|
||
stockPickerVisible: false,
|
||
stockSearch: '',
|
||
stockList: [],
|
||
selectedStocks: [],
|
||
}
|
||
},
|
||
computed: {
|
||
isEditable() {
|
||
return this.localMode === 'add' || this.localMode === 'edit'
|
||
},
|
||
},
|
||
async mounted() {
|
||
if (this.t_id) {
|
||
this.loadData()
|
||
} else {
|
||
this.localMode = 'add'
|
||
}
|
||
},
|
||
methods: {
|
||
async loadData() {
|
||
try {
|
||
let res = await this.$API.mpr.materialRequisition.item.req(this.t_id)
|
||
this.formData = res
|
||
const allItems = res.items_ || []
|
||
this.stockItems = allItems.filter(i => i.is_stock_item).map(i => ({
|
||
...i,
|
||
stock_id: i.stock,
|
||
stock_quantity: '—',
|
||
}))
|
||
this.nonStockItems = allItems.filter(i => !i.is_stock_item)
|
||
if (res.ticket_ && res.ticket_.state_.type === 1 && res.create_by === this.currentUser.id) {
|
||
this.localMode = 'edit'
|
||
}
|
||
} catch (error) {
|
||
this.$message.error('加载失败')
|
||
}
|
||
},
|
||
async openStockPicker() {
|
||
this.stockSearch = ''
|
||
this.selectedStocks = []
|
||
await this.loadStockList()
|
||
this.stockPickerVisible = true
|
||
},
|
||
async loadStockList() {
|
||
try {
|
||
let res = await this.$API.mpr.warehouseStock.list.req({ page: 0, search: this.stockSearch })
|
||
const existingIds = new Set(this.stockItems.map(i => i.stock_id))
|
||
this.stockList = (Array.isArray(res) ? res : res.results || []).filter(
|
||
s => Number(s.quantity) > 0 && !existingIds.has(s.id)
|
||
)
|
||
} catch (error) {
|
||
this.$message.error('加载库存失败')
|
||
}
|
||
},
|
||
handleStockSelectionChange(rows) {
|
||
this.selectedStocks = rows
|
||
},
|
||
confirmStockPick() {
|
||
this.selectedStocks.forEach(s => {
|
||
this.stockItems.push({
|
||
is_stock_item: true,
|
||
stock_id: s.id,
|
||
name: s.name,
|
||
spec: s.spec,
|
||
unit: s.unit,
|
||
stock_quantity: s.quantity,
|
||
quantity: 0,
|
||
req_type: '',
|
||
note: '',
|
||
})
|
||
})
|
||
this.stockPickerVisible = false
|
||
},
|
||
addNonStockItem() {
|
||
this.nonStockItems.push({
|
||
is_stock_item: false,
|
||
name: '',
|
||
spec: '',
|
||
unit: '',
|
||
quantity: 0,
|
||
req_type: '',
|
||
note: '',
|
||
})
|
||
},
|
||
delStockItem(index) {
|
||
this.stockItems.splice(index, 1)
|
||
},
|
||
delNonStockItem(index) {
|
||
this.nonStockItems.splice(index, 1)
|
||
},
|
||
validate() {
|
||
if (!this.formData.req_date) {
|
||
this.$message.warning('请选择填报时间')
|
||
return false
|
||
}
|
||
const allItems = [...this.stockItems, ...this.nonStockItems]
|
||
if (allItems.length === 0) {
|
||
this.$message.warning('请至少添加一项领用物品')
|
||
return false
|
||
}
|
||
for (let i = 0; i < this.stockItems.length; i++) {
|
||
const row = this.stockItems[i]
|
||
if (!row.quantity || Number(row.quantity) <= 0) {
|
||
this.$message.warning(`库存物品第${i + 1}行:领用量必须大于0`)
|
||
return false
|
||
}
|
||
if (Number(row.quantity) > Number(row.stock_quantity)) {
|
||
this.$message.warning(`库存物品第${i + 1}行:领用量(${row.quantity})超过库存量(${row.stock_quantity})`)
|
||
return false
|
||
}
|
||
}
|
||
for (let i = 0; i < this.nonStockItems.length; i++) {
|
||
const row = this.nonStockItems[i]
|
||
if (!row.name) {
|
||
this.$message.warning(`非清单物品第${i + 1}行:请填写物资名称`)
|
||
return false
|
||
}
|
||
if (!row.quantity || Number(row.quantity) <= 0) {
|
||
this.$message.warning(`非清单物品第${i + 1}行:领用量必须大于0`)
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
},
|
||
buildPayload() {
|
||
const items = []
|
||
this.stockItems.forEach(row => {
|
||
items.push({
|
||
is_stock_item: true,
|
||
stock: row.stock_id,
|
||
req_type: row.req_type,
|
||
name: row.name,
|
||
spec: row.spec,
|
||
unit: row.unit,
|
||
quantity: row.quantity,
|
||
note: row.note,
|
||
})
|
||
})
|
||
this.nonStockItems.forEach(row => {
|
||
items.push({
|
||
is_stock_item: false,
|
||
req_type: row.req_type,
|
||
name: row.name,
|
||
spec: row.spec,
|
||
unit: row.unit,
|
||
quantity: row.quantity,
|
||
note: row.note,
|
||
})
|
||
})
|
||
return {
|
||
req_date: this.formData.req_date,
|
||
collector: this.formData.collector,
|
||
note: this.formData.note,
|
||
items: items
|
||
}
|
||
},
|
||
async handleSubmit() {
|
||
if (!this.validate()) return
|
||
this.saveLoading = true
|
||
try {
|
||
if (this.localMode === 'add') {
|
||
await this.$API.mpr.materialRequisition.create.req(this.buildPayload())
|
||
this.$message.success('提交成功')
|
||
} else if (this.localMode === 'edit') {
|
||
await this.$API.mpr.materialRequisition.update.req(this.formData.id, this.buildPayload())
|
||
this.$message.success('更新并提交成功')
|
||
}
|
||
this.$emit('success')
|
||
} catch (error) {
|
||
const msg = error?.response?.data?.detail || error?.response?.data?.message || '提交失败'
|
||
this.$message.error(msg)
|
||
} finally {
|
||
this.saveLoading = false
|
||
}
|
||
},
|
||
handleDel() {
|
||
this.$confirm('确定删除该领用单吗?', '提示', { type: 'warning' })
|
||
.then(() => {
|
||
this.$API.mpr.materialRequisition.delete.req(this.formData.id).then(() => {
|
||
this.$message.success('删除成功')
|
||
this.$emit('success')
|
||
})
|
||
})
|
||
}
|
||
}
|
||
}
|
||
</script>
|