@@ -31,95 +31,10 @@
< / el-form >
< right-toolbar :showSearch.sync = "showSearch" @queryTable ="getList" > < / right -toolbar >
< el-button type = "primary" size = "mini" icon = "el-icon-plus" class = "add-purchase-btn"
@click ="addDialogVisible = true " > 新建采购单 < / el -button >
< add-purchase-dialog :visible.sync = "addDialogVisible" @saved ="getList" / >
@click ="$router.push('/oa/warehouse-data/addTask') " > 新建采购单 < / el -button >
< el-table v-loading = "loading" :data="TaskList" @selection-change="handleSelectionChange" stripe size="small"
row -key = " masterId " :expand-row-keys = "expandedKeys"
:row-class-name = "rowClassName" @ expand -change = " onRowExpand " >
< el-table-column type = "expand" width = "36" >
< template slot -scope = " props " >
< div style = "padding: 10px 24px; background:#fafafa;" >
<!-- 顶部 : 模式 + 批量入库 -- >
< div style = "display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;" >
< div style = "display:flex; align-items:center; gap:8px;" >
< span style = "font-weight:600;" > 物料明细 ( { { ( itemsMap [ props . row . masterId ] || [ ] ) . length } } 项 ) < / span >
< el-radio-group v-model = "mode" size="mini" v-if="props.row.status === 0" >
< el -radio -button label = "single" > 单个操作 < / el-radio-button >
< el-radio-button label = "batch" > 批量操作 < / el-radio-button >
< / el-radio-group >
< / div >
< div v-if = "mode === 'batch' && props.row.status === 0"
style = "display:flex; align-items:center; gap:6px;" >
< el-select v-model = "batchStatus" size="mini" placeholder="批量设置状态" style="width:140px" >
< el -option v-for = "s in statusOptions" :key="s.value" :value="s.value" :label="s.label" / >
< / el-select >
< el-button size = "mini" type = "success" @click ="submitComplete(props.row)" > 执行入库 < / el -button >
< / div >
< / div >
<!-- 收货单上传 : 仅入库批量场景需要 -- >
< div v-if = "mode === 'batch' && props.row.status === 0" class="receipt-row" >
< span class = "r-label" > < i class = "el-icon-paperclip" > < / i > 收货单 : < / span >
< file-upload v-model = "props.row.receiptDoc" / >
< span class = "r-hint" > 提交时会一并保存到本单据 < / span >
< / div >
< el-table v-loading = "itemsLoading[props.row.masterId]"
: data = "itemsMap[props.row.masterId] || []" size = "mini" stripe ref = "warehouseTable" >
< el-table-column v-if = "mode === 'batch' && props.row.status === 0"
type = "selection" width = "44" align = "center" / >
< el-table-column label = "物料名" prop = "name" min -width = " 120 " / >
< el-table-column label = "截止" prop = "endTime" width = "120" align = "center" >
< template slot -scope = " s " >
< template v-if = "s.row.endTime != null && s.row.taskStatus !== 2" >
< span v-if = "dayDiff(s.row.endTime) > 3" > {{ parseTime ( s.row.endTime , ' { y } - { m } - { d } ' ) }} < / span >
< el -tag v-else-if = "dayDiff(s.row.endTime) > 0" type="warning" size="mini" effect="plain" > 剩 {{ dayDiff ( s.row.endTime ) }} 天 < / el -tag >
< el-tag v-else-if = "dayDiff(s.row.endTime) === 0" type="danger" size="mini" effect="plain" > 今日 < / el -tag >
< el-tag v-else type = "danger" size = "mini" effect = "plain" > 逾 { { Math . abs ( dayDiff ( s . row . endTime ) ) } } 天 < / el-tag >
< / template >
< / template >
< / el-table-column >
< el-table-column label = "数量" prop = "taskInventory" width = "64" align = "right" / >
< el-table-column label = "单位" prop = "unit" width = "56" / >
< el-table-column label = "单价" width = "110" align = "right" >
< template slot -scope = " s " >
< el-input v-if = "s.row.taskStatus !== 2 && props.row.status === 0"
v-model = "s.row.price" size="mini" type="number" placeholder="¥" / >
< span v-else > ¥ {{ Number ( s.row.price | | 0 ) .toFixed ( 2 ) }} < / span >
< / template >
< / el -table -column >
< el-table-column label = "型号" prop = "model" min -width = " 100 " / >
< el-table-column label = "规格" prop = "specifications" min -width = " 100 " / >
< el-table-column label = "品牌" prop = "brand" min -width = " 80 " / >
< el-table-column label = "备注" prop = "remark" min -width = " 140 " >
< template slot -scope = " s " >
< el-input v-model = "s.row.remark" size="mini" placeholder="备注"
: disabled = "s.row.taskStatus === 2 || props.row.status === 1"
@blur ="updateRemark(s.row)" / >
< / template >
< / el-table-column >
< el-table-column label = "状态" prop = "taskStatus" width = "110" align = "center" >
< template slot -scope = " s " >
< el-tag v-if = "s.row.taskStatus === 2" type="success" size="mini" > 完成 < / el -tag >
< el-select v-else-if = "mode === 'single' && props.row.status === 0"
v-model = "s.row.taskStatus" size="mini" placeholder="状态"
@change ="handleUpdateTask(s.row)" >
< el -option v-for = "opt in filteredStatusOptions(s.row)" :key="opt.value"
:value = "opt.value" :label = "opt.label" / >
< / el-select >
< el-tag v-else size = "mini" > { { statusLabel ( s . row . taskStatus ) } } < / el-tag >
< / template >
< / el-table-column >
< el-table-column label = "操作" align = "center" width = "60" >
< template slot -scope = " s " >
< el-button v-if = "s.row.taskStatus !== 2 && props.row.status === 0"
size = "mini" type = "text" style = "color:#f56c6c"
@click ="handleBatchDelete(s.row)" > 删除 < / el -button >
< / template >
< / el-table-column >
< / el-table >
< / div >
< / template >
< / el-table-column >
row -key = " masterId " :row-class-name = "rowClassName" >
< el-table-column type = "selection" width = "44" align = "center" / >
< el-table-column label = "操作时间" prop = "signTime" width = "100" >
< template slot -scope = " scope " > { { parseTime ( scope . row . signTime , "{y}-{m}-{d}" ) } } < / template >
@@ -147,10 +62,12 @@
@blur ="updateMasterRemark(scope.row)" / >
< / template >
< / el-table-column >
< el-table-column label = "操作" align = "center" width = "22 0" class -name = " small -padding fixed -width " >
< el-table-column label = "操作" align = "center" width = "26 0" class -name = " small -padding fixed -width " >
< template slot -scope = " scope " >
< el-button size = "mini" type = "text" icon = "el-icon-search"
@click ="showDetail(scope.row)" > 查看 < / el -button >
< el-button size = "mini" type = "text" v-if = "scope.row.status === 0"
@click ="expandRow (scope.row)" > 执行入库 < / el -button >
@click ="showDetail (scope.row)" > 执行入库 < / el -button >
< el-button size = "mini" type = "text" v-if = "scope.row.status === 0"
@click ="handComplete(scope.row)" > 完成 < / el -button >
< el-button size = "mini" type = "text" @click ="handleExport(scope.row)" > 导出 < / el -button >
@@ -162,6 +79,93 @@
< pagination v-show = "total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination ="getList" / >
<!-- 物料明细抽屉 -- >
< el-drawer size = "70%"
: title = "(searchItem.masterNum || parseTime(searchItem.signTime, '{y}-{m}-{d}')) + ' - 物料明细'"
:visible.sync = "drawer" >
< div style = "padding: 0 16px 16px;" >
< div style = "display:flex; align-items:center; justify-content:space-between; margin-bottom:8px;" >
< div style = "display:flex; align-items:center; gap:8px;" >
< span style = "font-weight:600;" > 物料明细 ( { { warehouseTaskList . length } } 项 ) < / span >
< el-radio-group v-model = "mode" size="mini" v-if="searchItem.status === 0" >
< el -radio -button label = "single" > 单个操作 < / el-radio-button >
< el-radio-button label = "batch" > 批量操作 < / el-radio-button >
< / el-radio-group >
< / div >
< div v-if = "mode === 'batch' && searchItem.status === 0"
style = "display:flex; align-items:center; gap:6px;" >
< el-select v-model = "batchStatus" size="mini" placeholder="批量设置状态" style="width:140px" >
< el -option v-for = "s in statusOptions" :key="s.value" :value="s.value" :label="s.label" / >
< / el-select >
< el-button size = "mini" type = "success" @click ="submitComplete" > 执行入库 < / el -button >
< / div >
< / div >
< el-table v-loading = "loading" :data="warehouseTaskList" size="mini" stripe ref="warehouseTable" >
< el -table -column v-if = "mode === 'batch' && searchItem.status === 0"
type = "selection" width = "44" align = "center" / >
< el-table-column label = "物料名" prop = "name" min -width = " 120 " / >
< el-table-column label = "截止" prop = "endTime" width = "120" align = "center" >
< template slot -scope = " s " >
< template v-if = "s.row.endTime != null && s.row.taskStatus !== 2" >
< span v-if = "dayDiff(s.row.endTime) > 3" > {{ parseTime ( s.row.endTime , ' { y } - { m } - { d } ' ) }} < / span >
< el -tag v-else-if = "dayDiff(s.row.endTime) > 0" type="warning" size="mini" effect="plain" > 剩 {{ dayDiff ( s.row.endTime ) }} 天 < / el -tag >
< el-tag v-else-if = "dayDiff(s.row.endTime) === 0" type="danger" size="mini" effect="plain" > 今日 < / el -tag >
< el-tag v-else type = "danger" size = "mini" effect = "plain" > 逾 { { Math . abs ( dayDiff ( s . row . endTime ) ) } } 天 < / el-tag >
< / template >
< / template >
< / el-table-column >
< el-table-column label = "数量" prop = "taskInventory" width = "64" align = "right" / >
< el-table-column label = "单位" prop = "unit" width = "56" / >
< el-table-column label = "单价" width = "110" align = "right" >
< template slot -scope = " s " >
< el-input v-if = "s.row.taskStatus !== 2 && searchItem.status === 0"
v-model = "s.row.price" size="mini" type="number" placeholder="¥" / >
< span v-else > ¥ {{ Number ( s.row.price | | 0 ) .toFixed ( 2 ) }} < / span >
< / template >
< / el -table -column >
< el-table-column label = "型号" prop = "model" min -width = " 100 " / >
< el-table-column label = "规格" prop = "specifications" min -width = " 100 " / >
< el-table-column label = "品牌" prop = "brand" min -width = " 80 " / >
< el-table-column label = "备注" prop = "remark" min -width = " 140 " >
< template slot -scope = " s " >
< el-input v-model = "s.row.remark" size="mini" placeholder="备注"
: disabled = "s.row.taskStatus === 2 || searchItem.status === 1"
@blur ="updateRemark(s.row)" / >
< / template >
< / el-table-column >
< el-table-column label = "收货单" width = "240" >
< template slot -scope = " s " >
< file-upload v-model = "s.row.receiptDoc"
:limit = "3"
:isShowTip = "false"
: disabled = "s.row.taskStatus === 2 || searchItem.status === 1"
class = "cell-upload"
@input ="onReceiptDocChange(s.row)" / >
< / template >
< / el-table-column >
< el-table-column label = "状态" prop = "taskStatus" width = "110" align = "center" >
< template slot -scope = " s " >
< el-tag v-if = "s.row.taskStatus === 2" type="success" size="mini" > 完成 < / el -tag >
< el-select v-else-if = "mode === 'single' && searchItem.status === 0"
v-model = "s.row.taskStatus" size="mini" placeholder="状态"
@change ="handleUpdateTask(s.row)" >
< el -option v-for = "opt in filteredStatusOptions(s.row)" :key="opt.value"
:value = "opt.value" :label = "opt.label" / >
< / el-select >
< el-tag v-else size = "mini" > { { statusLabel ( s . row . taskStatus ) } } < / el-tag >
< / template >
< / el-table-column >
< el-table-column label = "操作" align = "center" width = "60" >
< template slot -scope = " s " >
< el-button v-if = "s.row.taskStatus !== 2 && searchItem.status === 0"
size = "mini" type = "text" style = "color:#f56c6c"
@click ="handleBatchDelete(s.row)" > 删除 < / el -button >
< / template >
< / el-table-column >
< / el-table >
< / div >
< / el-drawer >
< / div >
< / template >
@@ -192,11 +196,6 @@ export default {
// 顶部状态筛选
statusFilter : 'all' ,
stat : { undone : 0 , done : 0 , urgent : 0 } ,
// 物料明细缓存(按 masterId)
itemsMap : { } ,
itemsLoading : { } ,
// 当前展开的行(单展开)
expandedKeys : [ ] ,
// 新建采购单 dialog
addDialogVisible : false ,
completeDrawer : false ,
@@ -372,7 +371,7 @@ export default {
this . warehouseTaskList . splice ( index , 1 )
this . $message . success ( '删除成功' )
} ,
submitComplete ( masterRow ) {
submitComplete ( ) {
const rows = this . $refs . warehouseTable . selection || [ ]
if ( ! rows . length ) {
return this . $message . warning ( '请先勾选物料' )
@@ -383,21 +382,21 @@ export default {
rows . forEach ( r => { r . taskStatus = this . batchStatus } )
// 1. 如果有收货单 → 先保存到 master
const saveMaster = ( masterRow && masterRow . receiptDoc )
? updateOaWarehouseMaster ( {
masterId : masterRow . masterId ,
receiptDoc : masterRow . receiptDoc ,
type : masterRow . type
} )
: Promise . resolve ( )
saveMaster . then ( ( ) => updateOaWarehouseTaskBatch ( rows ) ) . then ( ( ) => {
updateOaWarehouseTaskBatch ( rows ) . then ( ( ) => {
this . getList ( )
this . drawer = false
this . $message . success ( ` 已批量入库 ${ rows . length } 条 ` )
} )
} ,
// 单条收货单上传后立刻保存
onReceiptDocChange ( row ) {
updateOaWarehouseTask ( {
taskId : row . taskId ,
receiptDoc : row . receiptDoc
} ) . then ( ( ) => {
this . $message . success ( '收货单已保存' )
} )
} ,
/** 执行入库操作 */
handleIn ( row ) {
// 更新采购单情况
@@ -443,8 +442,6 @@ export default {
this . TaskList = res . rows || [ ] ;
this . total = res . total || 0 ;
this . loading = false ;
// 重置已展开行的缓存
this . itemsMap = { } ;
} ) ;
this . refreshStat ( ) ;
} ,
@@ -485,40 +482,6 @@ export default {
this . queryParams . pageNum = 1
this . getList ( )
} ,
// 行展开:懒加载物料明细 + 同步 warehouseTaskList( 兼容老方法)
onRowExpand ( row , expanded ) {
// 单展开:只保留当前
if ( expanded && expanded . length ) {
this . expandedKeys = [ row . masterId ]
} else {
this . expandedKeys = this . expandedKeys . filter ( k => k !== row . masterId )
}
if ( ! expanded || ! expanded . length ) return
const id = row . masterId
this . currentMasterId = id
if ( this . itemsMap [ id ] ) {
this . warehouseTaskList = this . itemsMap [ id ]
return
}
this . $set ( this . itemsLoading , id , true )
getOaWarehouseTaskByMasterId ( id ) . then ( res => {
const list = res . data || res . rows || [ ]
this . $set ( this . itemsMap , id , list )
this . warehouseTaskList = list
} ) . finally ( ( ) => {
this . $set ( this . itemsLoading , id , false )
} )
} ,
// 程序化展开(用于"执行入库"按钮)
expandRow ( row ) {
if ( this . expandedKeys . includes ( row . masterId ) ) {
this . expandedKeys = this . expandedKeys . filter ( k => k !== row . masterId )
return
}
this . expandedKeys = [ row . masterId ]
// 触发数据加载
this . onRowExpand ( row , [ row . masterId ] )
} ,
// 取消按钮
cancel ( ) {
this . open = false ;
@@ -683,20 +646,61 @@ export default {
border - radius : 4 px ;
i { font - size : 11 px ; margin - right : 2 px ; }
}
. receipt - row {
display : flex ;
align - items : center ;
gap : 8 px ;
padding : 6 px 0 ;
margin - bottom : 6 px ;
border - bottom : 1 px dashed # ebeef5 ;
. r - label {
color : # 606266 ;
font - size : 12 px ;
flex - shrink : 0 ;
i { margin - right : 2 px ; color : # 909399 ; }
// 表格里的紧凑上传按钮 / 文件列表
. cell - upload {
: : v - deep . upload - file - uploader { margin - bottom : 4 px ; }
: : v - deep . el - upload . el - button {
padding : 4 px 8 px ! important ;
font - size : 12 px ! important ;
height : 24 px ;
line - height : 14 px ;
}
// 已上传文件列表
: : v - deep . upload - file - list {
margin : 0 ;
padding : 0 ;
. el - upload - list _ _item {
display : flex ;
align - items : center ;
justify - content : space - between ;
flex - wrap : nowrap ;
height : 24 px ;
line - height : 22 px ;
margin : 2 px 0 0 0 ! important ;
padding : 0 6 px ;
border : 1 px solid # ebeef5 ;
border - radius : 4 px ;
background : # fafafa ;
// 文件名:超出省略
. el - icon - document {
flex : 1 ;
min - width : 0 ;
overflow : hidden ;
text - overflow : ellipsis ;
white - space : nowrap ;
font - size : 12 px ;
color : # 303133 ;
padding - right : 6 px ;
}
// 操作按钮:纯图标,不换行
. ele - upload - list _ _item - content - action {
flex - shrink : 0 ;
display : inline - flex ;
align - items : center ;
gap : 2 px ;
. el - button {
padding : 0 4 px ! important ;
margin : 0 ! important ;
height : 20 px ;
line - height : 20 px ;
font - size : 0 ; // 隐藏文字
[ class ^= "el-icon-" ] {
font - size : 14 px ;
margin : 0 ;
}
}
}
}
}
. r - hint { color : # 909399 ; font - size : 11 px ; }
: : v - deep . el - upload - list { font - size : 12 px ; }
}
< / style >