feat:mpr-物资管理前端(申购单、入库单、物料库存、领用记录)

- 新增 mpr API 接口定义(申购单、入库单、物料库存、领用单 CRUD)
- 新增申购单列表页与表单页,支持明细内联编辑和一次性提交审批
- 新增入库单列表页与表单页,支持仓库下拉选择和入库明细管理
- 新增物料库存列表页,支持状态筛选(闲置/领用中/已领用/已领完)
- 新增领用记录列表页与表单页,支持从库存选择物品和非清单物品两类领用
- 注册物资管理路由菜单(申购单管理、入库单管理、物料库存、领用记录)

Made-with: Cursor
This commit is contained in:
TianyangZhang 2026-03-12 16:57:12 +08:00
parent a80f3b69ac
commit ab030d2487
9 changed files with 1639 additions and 0 deletions

114
src/api/model/mpr.js Normal file
View File

@ -0,0 +1,114 @@
import config from "@/config"
import http from "@/utils/request"
export default {
requisition: {
list: {
name: "申购单列表",
req: async function(data){
return await http.get(`${config.API_URL}/mpr/requisition/`, data);
}
},
create: {
name: "创建申购单",
req: async function(data){
return await http.post(`${config.API_URL}/mpr/requisition/`, data);
}
},
item: {
name: "获取申购单详情",
req: async function(id){
return await http.get(`${config.API_URL}/mpr/requisition/${id}/`);
}
},
update: {
name: "更新申购单",
req: async function(id, data){
return await http.put(`${config.API_URL}/mpr/requisition/${id}/`, data);
}
},
delete: {
name: "删除申购单",
req: async function(id){
return await http.delete(`${config.API_URL}/mpr/requisition/${id}/`);
}
},
},
warehouseStock: {
list: {
name: "物料库存列表",
req: async function(data){
return await http.get(`${config.API_URL}/mpr/warehouse_stock/`, data);
}
},
item: {
name: "获取库存详情",
req: async function(id){
return await http.get(`${config.API_URL}/mpr/warehouse_stock/${id}/`);
}
},
},
materialRequisition: {
list: {
name: "领用单列表",
req: async function(data){
return await http.get(`${config.API_URL}/mpr/material_requisition/`, data);
}
},
create: {
name: "创建领用单",
req: async function(data){
return await http.post(`${config.API_URL}/mpr/material_requisition/`, data);
}
},
item: {
name: "获取领用单详情",
req: async function(id){
return await http.get(`${config.API_URL}/mpr/material_requisition/${id}/`);
}
},
update: {
name: "更新领用单",
req: async function(id, data){
return await http.put(`${config.API_URL}/mpr/material_requisition/${id}/`, data);
}
},
delete: {
name: "删除领用单",
req: async function(id){
return await http.delete(`${config.API_URL}/mpr/material_requisition/${id}/`);
}
},
},
warehouseEntry: {
list: {
name: "入库单列表",
req: async function(data){
return await http.get(`${config.API_URL}/mpr/warehouse_entry/`, data);
}
},
create: {
name: "创建入库单",
req: async function(data){
return await http.post(`${config.API_URL}/mpr/warehouse_entry/`, data);
}
},
item: {
name: "获取入库单详情",
req: async function(id){
return await http.get(`${config.API_URL}/mpr/warehouse_entry/${id}/`);
}
},
update: {
name: "更新入库单",
req: async function(id, data){
return await http.put(`${config.API_URL}/mpr/warehouse_entry/${id}/`, data);
}
},
delete: {
name: "删除入库单",
req: async function(id){
return await http.delete(`${config.API_URL}/mpr/warehouse_entry/${id}/`);
}
},
}
}

View File

@ -2091,6 +2091,55 @@ const routes = [
},
],
},
//物资申购 mpr
{
name: "mpr",
path: "/mpr",
meta: {
title: "物资管理",
icon: "el-icon-document",
type: "menu",
perms: ["mpr"],
},
children: [
{
name: "requisition",
path: "/mpr/requisition",
meta: {
title: "申购单管理",
perms: ["requisition"],
},
component: "mpr/requisition",
},
{
name: "warehouse_entry",
path: "/mpr/warehouse_entry",
meta: {
title: "入库单管理",
perms: ["warehouse_entry"],
},
component: "mpr/warehouse_entry",
},
{
name: "warehouse_stock",
path: "/mpr/warehouse_stock",
meta: {
title: "物料库存",
perms: ["warehouse_stock"],
},
component: "mpr/warehouse_stock",
},
{
name: "material_requisition",
path: "/mpr/material_requisition",
meta: {
title: "领用记录",
perms: ["material_requisition"],
},
component: "mpr/material_requisition",
},
],
},
//采购 pum
{
name: "pum",

View File

@ -0,0 +1,128 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" v-auth="'material_requisition.create'">新增领用单</el-button>
</div>
<div class="right-panel">
<el-select v-model="query.belong_dept" placeholder="领用部门" clearable style="width: 140px; margin-right: 10px;" @change="handleQuery">
<el-option v-for="d in deptOptions" :key="d.id" :label="d.name" :value="d.id"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 260px; margin-right: 10px;"
@change="handleDateChange"
></el-date-picker>
<el-input v-model="query.search" placeholder="编号/操作人/领取人" clearable style="width: 200px;" @keyup.enter="handleQuery"></el-input>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable
ref="table"
:apiObj="$API.mpr.materialRequisition.list"
row-key="id"
stripe
:query="query"
@row-click="rowClick"
>
<el-table-column type="index" width="50" />
<el-table-column label="编号" prop="number" width="200" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="审批状态" width="200" show-overflow-tooltip>
<template #default="scope">
<template v-if="scope.row.ticket_">
<el-tag :type="actStateEnum[scope.row.ticket_?.act_state]?.type">
{{ actStateEnum[scope.row.ticket_?.act_state]?.text }}
</el-tag>
<el-tag type="info" effect="plain">{{ scope.row.ticket_?.state_?.name }}</el-tag>
</template>
<el-tag v-else type="info">未提交</el-tag>
</template>
</el-table-column>
<el-table-column label="领用部门" prop="belong_dept_name" width="120" show-overflow-tooltip></el-table-column>
<el-table-column label="填报时间" prop="req_date" width="120" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="领取人" prop="collector" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="操作人" prop="create_by_name" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="创建时间" prop="create_time" width="170" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="备注" prop="note" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="100">
<template #default="scope">
<el-button link type="danger" @click.stop="handleDel(scope.row)" v-auth="'material_requisition.delete'">删除</el-button>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<el-drawer title="领用记录详情" v-model="drawerVisible" :size="'85%'" destroy-on-close>
<requisition-form :mode="mode" :t_id="t_id" @success="handleSuccess"></requisition-form>
</el-drawer>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import API from '@/api'
import requisitionForm from './material_requisition_form.vue'
import { actStateEnum } from "@/utils/enum.js"
const table = ref(null)
const query = ref({})
const dateRange = ref(null)
const drawerVisible = ref(false)
const mode = ref('add')
const t_id = ref(null)
const deptOptions = ref([])
onMounted(async () => {
try {
let res = await API.system.dept.list.req({ page: 0 })
deptOptions.value = Array.isArray(res) ? res : (res.results || [])
} catch (e) {}
})
const handleAdd = () => {
mode.value = 'add'
t_id.value = null
drawerVisible.value = true
}
const rowClick = (row) => {
t_id.value = row.id
mode.value = 'show'
drawerVisible.value = true
}
const handleQuery = () => {
table.value.refresh()
}
const handleDateChange = (val) => {
if (val) {
query.value.req_date_after = val[0]
query.value.req_date_before = val[1]
} else {
delete query.value.req_date_after
delete query.value.req_date_before
}
handleQuery()
}
const handleDel = (row) => {
ElMessageBox.confirm('确定删除该领用记录吗?', '提示', { type: 'warning' })
.then(() => {
API.mpr.materialRequisition.delete.req(row.id).then(() => {
ElMessage.success('删除成功')
table.value.refresh()
})
})
}
const handleSuccess = () => {
drawerVisible.value = false
table.value.refresh()
}
</script>

View File

@ -0,0 +1,397 @@
<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>

View File

@ -0,0 +1,123 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" v-auth="'requisition.create'">新增申购</el-button>
</div>
<div class="right-panel">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 260px; margin-right: 10px;"
@change="handleDateChange"
></el-date-picker>
<el-input
v-model="query.search"
placeholder="编号/申购人"
clearable
style="width: 200px;"
@keyup.enter="handleQuery"
></el-input>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable
ref="table"
:apiObj="$API.mpr.requisition.list"
row-key="id"
stripe
:query="query"
@row-click="rowClick"
>
<el-table-column type="index" width="50" />
<el-table-column label="编号" prop="number" width="200" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="审批状态" width="200" show-overflow-tooltip>
<template #default="scope">
<template v-if="scope.row.ticket_">
<el-tag :type="actStateEnum[scope.row.ticket_?.act_state]?.type">
{{ actStateEnum[scope.row.ticket_?.act_state]?.text }}
</el-tag>
<el-tag type="info" effect="plain">{{ scope.row.ticket_?.state_?.name }}</el-tag>
</template>
<el-tag v-else type="info">未提交</el-tag>
</template>
</el-table-column>
<el-table-column label="申购部门" prop="belong_dept_name" width="120" show-overflow-tooltip></el-table-column>
<el-table-column label="申购人" prop="create_by_name" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="联系电话" prop="phone" width="130" show-overflow-tooltip></el-table-column>
<el-table-column label="申购日期" prop="req_date" width="120" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="合计金额" prop="total_amount" width="120" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="备注" prop="note" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="100">
<template #default="scope">
<el-button link type="danger" @click.stop="handleDel(scope.row)" v-auth="'requisition.delete'">删除</el-button>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<el-drawer title="物资申购单" v-model="drawerVisible" :size="'85%'" destroy-on-close>
<requisition-form :mode="mode" :t_id="t_id" @success="handleSuccess"></requisition-form>
</el-drawer>
</template>
<script setup>
import { ref } from 'vue'
import API from '@/api'
import requisitionForm from './requisition_form.vue'
import { actStateEnum } from "@/utils/enum.js"
const table = ref(null)
const query = ref({})
const dateRange = ref(null)
const drawerVisible = ref(false)
const mode = ref('add')
const t_id = ref(null)
const handleAdd = () => {
mode.value = 'add'
t_id.value = null
drawerVisible.value = true
}
const rowClick = (row) => {
t_id.value = row.id
mode.value = 'show'
drawerVisible.value = true
}
const handleQuery = () => {
table.value.refresh()
}
const handleDateChange = (val) => {
if (val) {
query.value.req_date_after = val[0]
query.value.req_date_before = val[1]
} else {
delete query.value.req_date_after
delete query.value.req_date_before
}
handleQuery()
}
const handleDel = (row) => {
ElMessageBox.confirm('确定删除该申购单吗?', '提示', { type: 'warning' })
.then(() => {
API.mpr.requisition.delete.req(row.id).then(() => {
ElMessage.success('删除成功')
table.value.refresh()
})
})
}
const handleSuccess = () => {
drawerVisible.value = false
table.value.refresh()
}
</script>

View File

@ -0,0 +1,279 @@
<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="申购人">{{ formData.create_by_name || currentUser.name }}</el-descriptions-item>
<el-descriptions-item label="联系电话">
<el-input v-if="isEditable" v-model="formData.phone" placeholder="联系电话" style="width: 200px;" />
<span v-else>{{ formData.phone }}</span>
</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="合计金额">
<span style="font-weight: bold; color: #e6a23c;">¥ {{ calcTotalAmount }}</span>
</el-descriptions-item>
<el-descriptions-item label="备注" :span="3">
<el-input v-if="isEditable" v-model="formData.note" type="textarea" :rows="2" 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="addItem">添加物品</el-button>
</div>
<el-table :data="items" border stripe style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column label="物品名称" min-width="120">
<template #default="scope">
<el-input v-if="isEditable" v-model="scope.row.item_name" placeholder="物品名称" />
<span v-else>{{ scope.row.item_name }}</span>
</template>
</el-table-column>
<el-table-column label="规格及型号" min-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="120">
<template #default="scope">
<el-input-number v-if="isEditable" v-model="scope.row.req_quantity" :min="0" :precision="3" size="small" controls-position="right" />
<span v-else>{{ scope.row.req_quantity }}</span>
</template>
</el-table-column>
<el-table-column label="现库存量" width="120">
<template #default="scope">
<el-input-number v-if="isEditable" v-model="scope.row.current_stock" :min="0" :precision="3" size="small" controls-position="right" />
<span v-else>{{ scope.row.current_stock }}</span>
</template>
</el-table-column>
<el-table-column label="需用日期" width="160">
<template #default="scope">
<el-date-picker v-if="isEditable" v-model="scope.row.need_date" type="date" placeholder="需用日期" value-format="YYYY-MM-DD" size="small" />
<span v-else>{{ scope.row.need_date }}</span>
</template>
</el-table-column>
<el-table-column label="需采购数量" width="130">
<template #default="scope">
<el-input-number v-if="isEditable" v-model="scope.row.purchase_quantity" :min="0" :precision="3" size="small" controls-position="right" />
<span v-else>{{ scope.row.purchase_quantity }}</span>
</template>
</el-table-column>
<el-table-column label="单价" width="120">
<template #default="scope">
<el-input-number v-if="isEditable" v-model="scope.row.unit_price" :min="0" :precision="2" size="small" controls-position="right" />
<span v-else>{{ scope.row.unit_price }}</span>
</template>
</el-table-column>
<el-table-column label="总价" width="110">
<template #default="scope">
<span style="font-weight: bold;">{{ calcRowTotal(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column label="备注" min-width="100">
<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="delItem(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="text-align: right; margin-top: 10px; font-size: 16px; font-weight: bold;">
合计¥ {{ calcTotalAmount }}
</div>
</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>
</template>
<script>
import ticketd_b from "@/views/wf/ticketd_b.vue"
import ticketd from '@/views/wf/ticketd.vue'
export default {
name: 'RequisitionForm',
components: { ticketd_b, ticketd },
props: {
mode: { type: String, default: 'show' },
t_id: { type: String, default: "" }
},
emits: ['success'],
data() {
return {
formData: {},
items: [],
localMode: this.mode,
saveLoading: false,
ticket_data: {},
currentUser: this.$TOOL.data.get("USER_INFO") || {}
}
},
computed: {
isEditable() {
return this.localMode === 'add' || this.localMode === 'edit'
},
calcTotalAmount() {
let total = 0
this.items.forEach(row => {
total += Number(this.calcRowTotal(row))
})
return total.toFixed(2)
}
},
mounted() {
if (this.t_id) {
this.loadData()
} else {
this.localMode = 'add'
}
},
methods: {
calcRowTotal(row) {
return (Number(row.purchase_quantity || 0) * Number(row.unit_price || 0)).toFixed(2)
},
async loadData() {
try {
let res = await this.$API.mpr.requisition.item.req(this.t_id)
this.formData = res
this.items = res.items_ || []
if (res.ticket_ && res.ticket_.state_.type === 1 && res.create_by === this.currentUser.id) {
this.localMode = 'edit'
}
} catch (error) {
this.$message.error('加载失败')
}
},
addItem() {
this.items.push({
item_name: '',
spec: '',
unit: '',
req_quantity: 0,
current_stock: 0,
need_date: null,
purchase_quantity: 0,
unit_price: 0,
total_price: 0,
note: ''
})
},
delItem(index) {
this.items.splice(index, 1)
},
validate() {
if (!this.formData.req_date) {
this.$message.warning('请选择申购日期')
return false
}
if (this.items.length === 0) {
this.$message.warning('请至少添加一项申购物品')
return false
}
for (let i = 0; i < this.items.length; i++) {
if (!this.items[i].item_name) {
this.$message.warning(`${i + 1}行:请填写物品名称`)
return false
}
}
return true
},
buildPayload() {
return {
phone: this.formData.phone,
req_date: this.formData.req_date,
note: this.formData.note,
items: this.items.map(row => ({
item_name: row.item_name,
spec: row.spec,
unit: row.unit,
req_quantity: row.req_quantity,
current_stock: row.current_stock,
need_date: row.need_date,
purchase_quantity: row.purchase_quantity,
unit_price: row.unit_price,
note: row.note
}))
}
},
async handleSubmit() {
if (!this.validate()) return
this.saveLoading = true
try {
if (this.localMode === 'add') {
await this.$API.mpr.requisition.create.req(this.buildPayload())
this.$message.success('提交成功')
} else if (this.localMode === 'edit') {
await this.$API.mpr.requisition.update.req(this.formData.id, this.buildPayload())
this.$message.success('更新并提交成功')
}
this.$emit('success')
} catch (error) {
this.$message.error('提交失败')
} finally {
this.saveLoading = false
}
},
handleDel() {
this.$confirm('确定删除该申购单吗?', '提示', { type: 'warning' })
.then(() => {
this.$API.mpr.requisition.delete.req(this.formData.id).then(() => {
this.$message.success('删除成功')
this.$emit('success')
})
})
}
}
}
</script>

View File

@ -0,0 +1,133 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<el-button type="primary" icon="el-icon-plus" @click="handleAdd" v-auth="'warehouse_entry.create'">新增入库单</el-button>
</div>
<div class="right-panel">
<el-select v-model="query.warehouse" placeholder="仓库" clearable style="width: 150px; margin-right: 10px;" @change="handleQuery">
<el-option v-for="w in warehouseOptions" :key="w.id" :label="w.name" :value="w.id"></el-option>
</el-select>
<el-select v-model="query.entry_type" placeholder="入库类型" clearable style="width: 150px; margin-right: 10px;" @change="handleQuery">
<el-option label="原材料正常入库" value="raw_normal"></el-option>
<el-option label="原材料暂估入库" value="raw_estimated"></el-option>
<el-option label="产品入库" value="product"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 260px; margin-right: 10px;"
@change="handleDateChange"
></el-date-picker>
<el-input v-model="query.search" placeholder="编号/操作人" clearable style="width: 180px;" @keyup.enter="handleQuery"></el-input>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable
ref="table"
:apiObj="$API.mpr.warehouseEntry.list"
row-key="id"
stripe
:query="query"
@row-click="rowClick"
>
<el-table-column type="index" width="50" />
<el-table-column label="编号" prop="number" width="200" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="审批状态" width="200" show-overflow-tooltip>
<template #default="scope">
<template v-if="scope.row.ticket_">
<el-tag :type="actStateEnum[scope.row.ticket_?.act_state]?.type">
{{ actStateEnum[scope.row.ticket_?.act_state]?.text }}
</el-tag>
<el-tag type="info" effect="plain">{{ scope.row.ticket_?.state_?.name }}</el-tag>
</template>
<el-tag v-else type="info">未提交</el-tag>
</template>
</el-table-column>
<el-table-column label="仓库" prop="warehouse_name" width="120" show-overflow-tooltip></el-table-column>
<el-table-column label="入库类型" prop="entry_type_display" width="140" show-overflow-tooltip></el-table-column>
<el-table-column label="入库方式" prop="entry_method_display" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="入库日期" prop="entry_date" width="120" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="操作人" prop="create_by_name" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="合计金额" prop="total_amount" width="120" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="备注" prop="note" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" fixed="right" align="center" width="100">
<template #default="scope">
<el-button link type="danger" @click.stop="handleDel(scope.row)" v-auth="'warehouse_entry.delete'">删除</el-button>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
<el-drawer title="仓库入库单" v-model="drawerVisible" :size="'85%'" destroy-on-close>
<entry-form :mode="mode" :t_id="t_id" @success="handleSuccess"></entry-form>
</el-drawer>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import API from '@/api'
import entryForm from './warehouse_entry_form.vue'
import { actStateEnum } from "@/utils/enum.js"
const table = ref(null)
const query = ref({})
const dateRange = ref(null)
const drawerVisible = ref(false)
const mode = ref('add')
const t_id = ref(null)
const warehouseOptions = ref([])
onMounted(async () => {
let res = await API.inm.warehouse.list.req({ page: 0 })
warehouseOptions.value = res
})
const handleAdd = () => {
mode.value = 'add'
t_id.value = null
drawerVisible.value = true
}
const rowClick = (row) => {
t_id.value = row.id
mode.value = 'show'
drawerVisible.value = true
}
const handleQuery = () => {
table.value.refresh()
}
const handleDateChange = (val) => {
if (val) {
query.value.entry_date_after = val[0]
query.value.entry_date_before = val[1]
} else {
delete query.value.entry_date_after
delete query.value.entry_date_before
}
handleQuery()
}
const handleDel = (row) => {
ElMessageBox.confirm('确定删除该入库单吗?', '提示', { type: 'warning' })
.then(() => {
API.mpr.warehouseEntry.delete.req(row.id).then(() => {
ElMessage.success('删除成功')
table.value.refresh()
})
})
}
const handleSuccess = () => {
drawerVisible.value = false
table.value.refresh()
}
</script>

View File

@ -0,0 +1,304 @@
<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="仓库名称">
<el-select v-if="isEditable" v-model="formData.warehouse" placeholder="选择仓库" style="width: 200px;">
<el-option v-for="w in warehouseOptions" :key="w.id" :label="w.name" :value="w.id"></el-option>
</el-select>
<span v-else>{{ formData.warehouse_name }}</span>
</el-descriptions-item>
<el-descriptions-item label="入库日期">
<el-date-picker
v-if="isEditable"
v-model="formData.entry_date"
type="date"
placeholder="入库日期"
value-format="YYYY-MM-DD"
style="width: 200px;"
/>
<span v-else>{{ formData.entry_date }}</span>
</el-descriptions-item>
<el-descriptions-item label="入库类型">
<el-select v-if="isEditable" v-model="formData.entry_type" placeholder="入库类型" style="width: 200px;">
<el-option label="原材料正常入库" value="raw_normal"></el-option>
<el-option label="原材料暂估入库" value="raw_estimated"></el-option>
<el-option label="产品入库" value="product"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
<span v-else>{{ formData.entry_type_display }}</span>
</el-descriptions-item>
<el-descriptions-item label="入库方式">
<el-select v-if="isEditable" v-model="formData.entry_method" placeholder="入库方式" style="width: 200px;">
<el-option label="采购" value="purchase"></el-option>
<el-option label="自制" value="self_made"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
<span v-else>{{ formData.entry_method_display }}</span>
</el-descriptions-item>
<el-descriptions-item label="合计金额">
<span style="font-weight: bold; color: #e6a23c;">¥ {{ calcTotalAmount }}</span>
</el-descriptions-item>
<el-descriptions-item label="操作人">{{ formData.create_by_name || currentUser.name }}</el-descriptions-item>
<el-descriptions-item label="所属部门">{{ formData.belong_dept_name || currentUser.belong_dept_name }}</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="addItem">添加物品</el-button>
</div>
<el-table :data="items" border stripe style="width: 100%">
<el-table-column type="index" label="序号" width="60" align="center" />
<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="120">
<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="单价" width="120">
<template #default="scope">
<el-input-number v-if="isEditable" v-model="scope.row.unit_price" :min="0" :precision="2" size="small" controls-position="right" />
<span v-else>{{ scope.row.unit_price }}</span>
</template>
</el-table-column>
<el-table-column label="金额" width="110">
<template #default="scope">
<span style="font-weight: bold;">{{ calcRowAmount(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column label="供应商名称" min-width="120">
<template #default="scope">
<el-input v-if="isEditable" v-model="scope.row.supplier_name" placeholder="供应商" />
<span v-else>{{ scope.row.supplier_name }}</span>
</template>
</el-table-column>
<el-table-column label="账单是否收到" width="120" align="center">
<template #default="scope">
<el-checkbox v-if="isEditable" v-model="scope.row.invoice_received" />
<el-tag v-else :type="scope.row.invoice_received ? 'success' : 'danger'" size="small">
{{ scope.row.invoice_received ? '已收到' : '未收到' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="备注" min-width="100">
<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="delItem(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div style="text-align: right; margin-top: 10px; font-size: 16px; font-weight: bold;">
合计¥ {{ calcTotalAmount }}
</div>
</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>
</template>
<script>
import ticketd_b from "@/views/wf/ticketd_b.vue"
import ticketd from '@/views/wf/ticketd.vue'
export default {
name: 'WarehouseEntryForm',
components: { ticketd_b, ticketd },
props: {
mode: { type: String, default: 'show' },
t_id: { type: String, default: "" }
},
emits: ['success'],
data() {
return {
formData: {
entry_type: 'raw_normal',
entry_method: 'purchase',
},
items: [],
localMode: this.mode,
saveLoading: false,
ticket_data: {},
currentUser: this.$TOOL.data.get("USER_INFO") || {},
warehouseOptions: []
}
},
computed: {
isEditable() {
return this.localMode === 'add' || this.localMode === 'edit'
},
calcTotalAmount() {
let total = 0
this.items.forEach(row => {
total += Number(this.calcRowAmount(row))
})
return total.toFixed(2)
}
},
async mounted() {
let res = await this.$API.inm.warehouse.list.req({ page: 0 })
this.warehouseOptions = res
if (this.t_id) {
this.loadData()
} else {
this.localMode = 'add'
}
},
methods: {
calcRowAmount(row) {
return (Number(row.quantity || 0) * Number(row.unit_price || 0)).toFixed(2)
},
async loadData() {
try {
let res = await this.$API.mpr.warehouseEntry.item.req(this.t_id)
this.formData = res
this.items = res.items_ || []
if (res.ticket_ && res.ticket_.state_.type === 1 && res.create_by === this.currentUser.id) {
this.localMode = 'edit'
}
} catch (error) {
this.$message.error('加载失败')
}
},
addItem() {
this.items.push({
name: '',
spec: '',
unit: '',
quantity: 0,
unit_price: 0,
amount: 0,
supplier_name: '',
invoice_received: false,
note: ''
})
},
delItem(index) {
this.items.splice(index, 1)
},
validate() {
if (!this.formData.warehouse) {
this.$message.warning('请选择仓库')
return false
}
if (!this.formData.entry_date) {
this.$message.warning('请选择入库日期')
return false
}
if (this.items.length === 0) {
this.$message.warning('请至少添加一项入库物品')
return false
}
for (let i = 0; i < this.items.length; i++) {
if (!this.items[i].name) {
this.$message.warning(`${i + 1}行:请填写名称`)
return false
}
}
return true
},
buildPayload() {
return {
warehouse: this.formData.warehouse,
entry_date: this.formData.entry_date,
entry_type: this.formData.entry_type,
entry_method: this.formData.entry_method,
note: this.formData.note,
items: this.items.map(row => ({
name: row.name,
spec: row.spec,
unit: row.unit,
quantity: row.quantity,
unit_price: row.unit_price,
supplier_name: row.supplier_name,
invoice_received: row.invoice_received,
note: row.note
}))
}
},
async handleSubmit() {
if (!this.validate()) return
this.saveLoading = true
try {
if (this.localMode === 'add') {
await this.$API.mpr.warehouseEntry.create.req(this.buildPayload())
this.$message.success('提交成功')
} else if (this.localMode === 'edit') {
await this.$API.mpr.warehouseEntry.update.req(this.formData.id, this.buildPayload())
this.$message.success('更新并提交成功')
}
this.$emit('success')
} catch (error) {
this.$message.error('提交失败')
} finally {
this.saveLoading = false
}
},
handleDel() {
this.$confirm('确定删除该入库单吗?', '提示', { type: 'warning' })
.then(() => {
this.$API.mpr.warehouseEntry.delete.req(this.formData.id).then(() => {
this.$message.success('删除成功')
this.$emit('success')
})
})
}
}
}
</script>

View File

@ -0,0 +1,112 @@
<template>
<el-container>
<el-header>
<div class="left-panel"></div>
<div class="right-panel">
<el-select v-model="query.warehouse" placeholder="仓库" clearable style="width: 150px; margin-right: 10px;" @change="handleQuery">
<el-option v-for="w in warehouseOptions" :key="w.id" :label="w.name" :value="w.id"></el-option>
</el-select>
<el-select v-model="query.entry_type" placeholder="入库类型" clearable style="width: 150px; margin-right: 10px;" @change="handleQuery">
<el-option label="原材料正常入库" value="raw_normal"></el-option>
<el-option label="原材料暂估入库" value="raw_estimated"></el-option>
<el-option label="产品入库" value="product"></el-option>
<el-option label="其他" value="other"></el-option>
</el-select>
<el-select v-model="query.status" placeholder="物料状态" clearable style="width: 120px; margin-right: 10px;" @change="handleQuery">
<el-option label="闲置" value="idle"></el-option>
<el-option label="领用中" value="in_requisition"></el-option>
<el-option label="已领用" value="requisitioned"></el-option>
</el-select>
<el-select v-model="query.invoice_received" placeholder="账单状态" clearable style="width: 120px; margin-right: 10px;" @change="handleQuery">
<el-option label="已收到" :value="true"></el-option>
<el-option label="未收到" :value="false"></el-option>
</el-select>
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 260px; margin-right: 10px;"
@change="handleDateChange"
></el-date-picker>
<el-input v-model="query.search" placeholder="名称/规格/供应商/单号" clearable style="width: 220px;" @keyup.enter="handleQuery"></el-input>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<scTable
ref="table"
:apiObj="$API.mpr.warehouseStock.list"
row-key="id"
stripe
:query="query"
>
<el-table-column type="index" width="50" />
<el-table-column label="仓库" prop="warehouse_name" width="120" show-overflow-tooltip></el-table-column>
<el-table-column label="入库单号" prop="entry_number" width="200" show-overflow-tooltip></el-table-column>
<el-table-column label="入库日期" prop="entry_date" width="110" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="入库类型" prop="entry_type_display" width="140" show-overflow-tooltip></el-table-column>
<el-table-column label="入库方式" prop="entry_method_display" width="100" show-overflow-tooltip></el-table-column>
<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="70" show-overflow-tooltip></el-table-column>
<el-table-column label="数量" prop="quantity" width="100" sortable show-overflow-tooltip></el-table-column>
<el-table-column label="单价" prop="unit_price" width="100" show-overflow-tooltip></el-table-column>
<el-table-column label="金额" prop="amount" width="110" sortable show-overflow-tooltip>
<template #default="scope">
<span style="font-weight: bold;">{{ scope.row.amount }}</span>
</template>
</el-table-column>
<el-table-column label="状态" width="100" align="center">
<template #default="scope">
<el-tag v-if="Number(scope.row.quantity) <= 0" type="info" size="small">已领完</el-tag>
<el-tag v-else-if="scope.row.status === 'idle'" type="success" size="small">闲置</el-tag>
<el-tag v-else-if="scope.row.status === 'in_requisition'" type="warning" size="small">领用中</el-tag>
<el-tag v-else-if="scope.row.status === 'requisitioned'" type="danger" size="small">已领用</el-tag>
<el-tag v-else type="info" size="small">{{ scope.row.status_display }}</el-tag>
</template>
</el-table-column>
<el-table-column label="供应商" prop="supplier_name" width="140" show-overflow-tooltip></el-table-column>
<el-table-column label="账单" width="90" align="center">
<template #default="scope">
<el-tag :type="scope.row.invoice_received ? 'success' : 'danger'" size="small">
{{ scope.row.invoice_received ? '已收到' : '未收到' }}
</el-tag>
</template>
</el-table-column>
</scTable>
</el-main>
</el-container>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import API from '@/api'
const table = ref(null)
const query = ref({})
const dateRange = ref(null)
const warehouseOptions = ref([])
onMounted(async () => {
let res = await API.inm.warehouse.list.req({ page: 0 })
warehouseOptions.value = res
})
const handleQuery = () => {
table.value.refresh()
}
const handleDateChange = (val) => {
if (val) {
query.value.entry_date_after = val[0]
query.value.entry_date_before = val[1]
} else {
delete query.value.entry_date_after
delete query.value.entry_date_before
}
handleQuery()
}
</script>