Commit 5fb48cf8 by zhangtw

入库、申领

parent d3c15121
......@@ -16,14 +16,14 @@
</div>
<Table :data="tables.inbound" :loading="loading.inbound" :columns="inboundColumns" border>
<template slot="action" slot-scope="{ row }">
<Poptip confirm title="确认执行入库?" transfer @on-ok="doInbound(row)" v-if="row.inbound_status === 0">
<Button size="small" type="success">入库</Button>
</Poptip>
<Button size="small" @click="openDetail(row)">详细</Button>
<Button size="small" type="primary" @click="openEdit(row)" v-if="canEdit(row)">修改</Button>
<Poptip confirm title="确认删除?" transfer @on-ok="deleteInbound(row)">
<Button size="small" type="error">删除</Button>
</Poptip>
<Poptip confirm title="确认执行入库?" transfer @on-ok="doInbound(row)" v-if="row.inbound_status===0">
<Button size="small" type="success">入库</Button>
</Poptip>
</template>
</Table>
<Page class="page_style" :total="pagers.inbound.totalRecord" :current="pagers.inbound.pageNo" :page-size="pagers.inbound.pageSize"
......@@ -73,7 +73,13 @@
<Input v-model="inboundModal.form.batch_no" />
</FormItem>
<FormItem label="入库日期">
<DatePicker v-model="inboundModal.form.inbound_date" type="date" />
<DatePicker
v-model="inboundModal.form.inbound_date"
type="date"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
placeholder="请选择入库日期"
/>
</FormItem>
<FormItem label="存放位置">
<Input v-model="inboundModal.form.storage_location" />
......@@ -145,6 +151,9 @@ export default {
components: { MaterialSelector },
data () {
return {
// 入库状态映射字典
inboundTypeMap: { '1': '手工', '2': '归还' },
inboundStatusMap: { '0': '待入库', '1': '已入库' },
activeTab: 'inbound',
filters: { inbound: { inbound_no: '' }, return: { key: '' }, inventory: { material_name: '' } },
tables: { inbound: [], return: [], inventory: [] },
......@@ -154,9 +163,30 @@ export default {
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '入库单号', key: 'inbound_no', align: 'center' },
{ title: '批次号', key: 'batch_no', align: 'center' },
{ title: '入库日期', key: 'inbound_date', align: 'center' },
{ title: '类型', key: 'inbound_type', align: 'center' },
{ title: '状态', key: 'inbound_status', align: 'center' },
{
title: '入库日期',
key: 'inbound_date',
align: 'center',
render: (h, { row }) => {
return h('span', this.formatDate(row.inbound_date) || row.inbound_date || '-')
}
},
{
title: '类型',
key: 'inbound_type',
align: 'center',
render: (h, { row }) => {
return h('span', this.inboundTypeMap[row.inbound_type] || row.inbound_type || '-')
}
},
{
title: '状态',
key: 'inbound_status',
align: 'center',
render: (h, { row }) => {
return h('span', this.inboundStatusMap[row.inbound_status] || row.inbound_status || '-')
}
},
{ title: '操作', slot: 'action', width: 360, align: 'center' }
],
returnColumns: [
......@@ -180,7 +210,10 @@ export default {
{ type: 'selection', width: 60 },
{ title: '物料编码', key: 'material_code', minWidth: 120 },
{ title: '物料名称', key: 'material_name', minWidth: 150 },
{ title: '入库数量', key: 'inbound_quantity', minWidth: 120, render: (h, params) => {
{ title: '入库数量',
key: 'inbound_quantity',
minWidth: 120,
render: (h, params) => {
return h('InputNumber', {
props: {
value: params.row.inbound_quantity || 0,
......@@ -189,14 +222,22 @@ export default {
},
style: { width: '100px' },
on: {
input: (val) => {
params.row.inbound_quantity = val || 0
params.row.total_amount = ((params.row.inbound_quantity || 0) * (params.row.unit_price || 0)).toFixed(2)
'on-change': (val) => {
// 使用Vue.set确保响应式更新
this.$set(this.inboundModal.details[params.index], 'inbound_quantity', Number(val) || 0)
// 计算并更新总金额
const quantity = Number(val) || 0
const unitPrice = this.inboundModal.details[params.index].unit_price || 0
this.$set(this.inboundModal.details[params.index], 'total_amount', (quantity * unitPrice).toFixed(2))
}
}
})
}},
{ title: '单价', key: 'unit_price', minWidth: 120, render: (h, params) => {
}
},
{ title: '单价',
key: 'unit_price',
minWidth: 120,
render: (h, params) => {
return h('InputNumber', {
props: {
value: params.row.unit_price || 0,
......@@ -205,17 +246,30 @@ export default {
},
style: { width: '100px' },
on: {
input: (val) => {
params.row.unit_price = val || 0
params.row.total_amount = ((params.row.inbound_quantity || 0) * (params.row.unit_price || 0)).toFixed(2)
'on-change': (val) => {
// 使用Vue.set确保响应式更新
this.$set(this.inboundModal.details[params.index], 'unit_price', Number(val) || 0)
// 计算并更新总金额
const quantity = this.inboundModal.details[params.index].inbound_quantity || 0
const unitPrice = Number(val) || 0
this.$set(this.inboundModal.details[params.index], 'total_amount', (quantity * unitPrice).toFixed(2))
}
}
})
}},
{ title: '总金额', key: 'total_amount', minWidth: 120, render: (h, params) => {
const amount = ((params.row.inbound_quantity || 0) * (params.row.unit_price || 0)).toFixed(2)
return h('span', amount)
}}
}
},
{
title: '总金额',
key: 'total_amount',
minWidth: 120,
render: (h, params) => {
const quantity = Number(params.row.inbound_quantity) || 0
const unitPrice = Number(params.row.unit_price) || 0
const total = (quantity * unitPrice).toFixed(2)
this.$set(params.row, 'total_amount', total)
return h('span', total)
}
}
],
returnDetailColumns: [
{ title: '物料名称', key: 'material_name' },
......@@ -243,6 +297,14 @@ export default {
},
created () { this.fetchList('inbound') },
methods: {
// 获取当天日期,格式为 yyyy-MM-dd
getTodayDate () {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
handleTabChange (name) {
this.activeTab = name
if (name === 'return') this.fetchList('return')
......@@ -268,7 +330,7 @@ export default {
openInboundModal () {
this.inboundModal.isEdit = false
this.inboundModal.form = { inbound_no: '', batch_no: '', inbound_date: '', inbound_type: 1, storage_location: '', remark: '' }
this.inboundModal.form = { inbound_no: '', batch_no: '', inbound_date: this.getTodayDate(), inbound_type: 1, storage_location: '', remark: '' }
this.inboundModal.details = []
this.inboundModal.visible = true
},
......@@ -276,6 +338,7 @@ export default {
this.inboundSelectedDetails = list || []
},
handleInboundMaterialSelectorOk (selectedRows) {
console.log('选择的物料信息' + selectedRows)
if (!Array.isArray(selectedRows) || selectedRows.length === 0) {
this.$Message.warning('未选择物料')
return
......@@ -287,6 +350,7 @@ export default {
exist.inbound_quantity = Number(exist.inbound_quantity || 0) + 1
} else {
this.inboundModal.details.push({
material_id: sel.id,
material_code: sel.material_code,
material_name: sel.material_name,
inbound_quantity: 0,
......@@ -297,6 +361,12 @@ export default {
})
this.showMaterialSelector = false
},
// 转换时间戳为yyyy-MM-dd
formatDate (timestamp) {
if (!timestamp || isNaN(Number(timestamp))) return ''
const d = new Date(Number(timestamp))
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
},
deleteSelectedInboundDetails () {
if (!this.inboundSelectedDetails || this.inboundSelectedDetails.length === 0) {
this.$Message.warning('请先选择要删除的明细行')
......@@ -311,21 +381,83 @@ export default {
getInboundById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.inboundModal.form = ret.data.data || {}
if (this.inboundModal.form.inbound_date) {
this.inboundModal.form.inbound_date = this.formatDate(this.inboundModal.form.inbound_date) || ''
} else {
this.inboundModal.form.inbound_date = this.getTodayDate()
}
this.inboundModal.details = ret.data.data.details || []
}
})
this.inboundModal.visible = true
},
saveInbound () {
// 1. 验证明细数据
this.inboundModal.saving = true
saveInbound(Object.assign({}, this.inboundModal.form, { details: this.inboundModal.details })).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('保存成功'); this.inboundModal.visible = false; this.fetchList('inbound') } else this.$Notice.error({ title: '保存失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.inboundModal.saving = false })
// 1. 验证主表必填项
const { inbound_no, batch_no, inbound_date } = this.inboundModal.form
if (!inbound_no || !batch_no || !inbound_date) {
this.$Message.warning('入库单号、批次号、入库日期为必填项')
this.inboundModal.saving = false
return
}
// 2. 验证明细数据
const invalidDetails = this.inboundModal.details.filter(detail => {
return !detail.inbound_quantity || detail.inbound_quantity <= 0
})
if (invalidDetails.length > 0) {
this.$Message.warning('请填写有效的入库数量(必须大于0)')
this.inboundModal.saving = false
return
}
// 2. 转换数据类型
const details = this.inboundModal.details.map(detail => {
return {
material_id: detail.material_id,
material_code: detail.material_code,
material_name: detail.material_name,
inbound_quantity: Number(detail.inbound_quantity) || 0,
unit_price: Number(detail.unit_price) || 0,
total_amount: Number(detail.total_amount) || 0
}
})
// 3. 发送请求
const payload = {
...this.inboundModal.form,
details: details
}
console.log('发送的数据:', payload) // 添加日志,查看发送的数据
saveInbound(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('保存成功')
this.inboundModal.visible = false
this.fetchList('inbound')
} else {
this.$Notice.error({
title: '保存失败',
desc: ret.data & ret.data.errmsg || '未知错误'
})
}
}).catch(error => {
console.error('保存出错:', error)
this.$Notice.error({ title: '保存出错', desc: error.message })
}).finally(() => {
this.inboundModal.saving = false
})
},
canEdit (row) { return row.inbound_status === 0 },
deleteInbound (row) { deleteInbound({ id: row.id }).then(ret => { if (ret.data && ret.data.errcode === 0) { this.$Message.success('删除成功'); this.fetchList('inbound') } else this.$Notice.error({ title: '删除失败', desc: ret.data && ret.data.errmsg }) }) },
doInbound (row) { doInbound({ id: row.id }).then(ret => { if (ret.data && ret.data.errcode === 0) { this.$Message.success('入库成功'); this.fetchList('inbound'); this.fetchList('inventory') } else this.$Notice.error({ title: '入库失败', desc: ret.data && ret.data.errmsg }) }) },
doInbound (row) {
doInbound({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('入库成功')
this.fetchList('inbound')
this.fetchList('inventory')
} else {
this.$Notice.error({ title: '入库失败', desc: ret.data && ret.data.errmsg })
}
})
},
openReturnModal (row) {
this.returnModal.record = Object.assign({}, row)
this.returnModal.details = []
......@@ -346,6 +478,7 @@ export default {
this.detailModal.loading = true
getInboundById({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
console.log(ret.data)
this.detailModal.data = ret.data.data || {}
this.detailModal.details = ret.data.data.details || []
this.detailModal.logs = ret.data.data.logs || []
......
......@@ -215,6 +215,9 @@ export default {
components: { MaterialSelector },
data () {
return {
// 申请弹窗中的可编辑行状态
editingRowIndex: -1,
editingCellField: '',
// 审批状态映射字典
approvalStatusMap: { '0': '待提交', '1': '审核中', '9': '审核通过', '-1': '驳回' },
activeTab: 'apply',
......@@ -248,7 +251,14 @@ export default {
{ title: '申请单号', key: 'application_no', align: 'center' },
{ title: '申请人', key: 'applicant_name', align: 'center' },
{ title: '部门', key: 'department_name', align: 'center' },
{ title: '提交时间', key: 'submit_time', align: 'center' },
{
title: '提交时间',
key: 'submit_time',
align: 'center',
render: (h, { row }) => {
return h('span', this.formatDate(row.submit_time) || row.submit_time || '-')
}
},
{ title: '操作', slot: 'action', width: 120, align: 'center' }
],
historyColumns: [
......@@ -271,7 +281,77 @@ export default {
{ type: 'selection', width: 60 },
{ title: '物料编码', key: 'material_code' },
{ title: '物料名称', key: 'material_name' },
{ title: '申请数量', key: 'apply_quantity' },
{
title: '申请数量',
key: 'apply_quantity',
render: (h, { row, column, index }) => {
// 如果当前行是编辑状态,显示输入框
if (this.editingRowIndex === index && this.editingCellField === 'apply_quantity') {
return h('InputNumber', {
props: {
value: row.apply_quantity,
min: 1,
max: row.available_quantity || 9999,
precision: 0
},
style: { width: '100%' },
on: {
input: (value) => {
// 更新数据
this.applyModal.details[index].apply_quantity = value
},
'on-blur': () => {
// 失去焦点时退出编辑模式
this.editingRowIndex = -1
this.editingCellField = ''
},
'on-keyup': (event) => {
// 按回车键保存并退出编辑
if (event.keyCode === 13) {
this.editingRowIndex = -1
this.editingCellField = ''
}
}
}
})
} else {
// 非编辑状态,显示文本和编辑图标
return h('div', {
style: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
cursor: 'pointer',
padding: '0 8px'
},
on: {
click: () => {
// 点击进入编辑模式
this.editingRowIndex = index
this.editingCellField = 'apply_quantity'
// 下一个tick聚焦到输入框
this.$nextTick(() => {
const input = document.querySelector(`.ivu-table-cell-with-input input`)
if (input) input.focus()
})
}
}
}, [
h('span', row.apply_quantity || 0),
h('Icon', {
props: {
type: 'md-create',
size: 14
},
style: {
color: '#2d8cf0',
marginLeft: '8px'
}
})
])
}
}
},
{ title: '单位', key: 'unit' },
{ title: '备注', key: 'issue_remark' }
],
......@@ -297,16 +377,41 @@ export default {
// 选择器控制与已选明细
showMaterialSelector: false,
applySelectedDetails: [],
applyModal: { visible: false, isEdit: false, saving: false, form: {}, details: [] },
applyModal: { visible: false, isEdit: false, saving: false, form: {}, details: [], isEditing: false },
approveModal: { visible: false, record: {}, details: [], opinion: '', submitting: false },
detailModal: { visible: false, loading: false, data: {}, details: [], logs: [] }
}
},
created () { this.fetchList('apply') },
methods: {
// 获取当天日期,格式为 yyyy-MM-dd
getTodayDate () {
const today = new Date()
const year = today.getFullYear()
const month = String(today.getMonth() + 1).padStart(2, '0')
const day = String(today.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
// 处理单元格点击编辑
handleCellEdit (row, column, index, field) {
if (field === 'apply_quantity') {
this.editingRowIndex = index
this.editingCellField = field
// 下一个tick聚焦到输入框
this.$nextTick(() => {
const input = document.querySelector(`.ivu-table-cell-with-input input`)
if (input) input.focus()
})
}
},
// 添加方法:退出编辑模式
exitEditMode () {
this.editingRowIndex = -1
this.editingCellField = ''
},
// 转换时间戳为yyyy-MM-dd
formatDate (timestamp) {
if (!timestamp || isNaN(Number(timestamp))) return '未设置'
if (!timestamp || isNaN(Number(timestamp))) return ''
const d = new Date(Number(timestamp))
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
},
......@@ -350,9 +455,10 @@ export default {
openApplyModal () {
this.applyModal.isEdit = false
this.applyModal.form = { applicant_id: '', applicant_name: '', department_id: '', department_name: '', borrow_purpose: '', expected_return_date: '', approval_status: 0 }
this.applyModal.form = { applicant_id: '', applicant_name: '', department_id: '', department_name: '', borrow_purpose: '', expected_return_date: this.getTodayDate(), approval_status: 0 }
this.applyModal.details = []
this.applyModal.visible = true
this.exitEditMode()
},
openEdit (row) {
console.log(row)
......@@ -363,7 +469,7 @@ export default {
this.applyModal.form.expected_return_date = this.formatDate(this.applyModal.form.expected_return_date)
} else {
// 空值兜底
this.applyModal.form.expected_return_date = ''
this.applyModal.form.expected_return_date = this.getTodayDate()
}
// load details from backend when open (simplified)
getBorrowById({ id: row.id }).then(ret => {
......@@ -372,6 +478,7 @@ export default {
}
})
this.applyModal.visible = true
this.exitEditMode()
},
onApplyDetailSelectionChange (list) {
this.applySelectedDetails = list || []
......@@ -409,6 +516,7 @@ export default {
this.applySelectedDetails = []
},
saveApplication () {
this.exitEditMode()
this.applyModal.saving = true
const payload = Object.assign({}, this.applyModal.form, { details: this.applyModal.details })
saveBorrowApplication(payload).then(ret => {
......@@ -536,4 +644,17 @@ export default {
padding: 16px 0 0 0;
}
}
/* 添加一些样式来美化编辑状态 */
.ivu-table-cell {
position: relative;
}
.edit-icon {
opacity: 0;
transition: opacity 0.2s;
}
.ivu-table-row:hover .edit-icon {
opacity: 1;
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论