master
lion 1 month ago
parent 3a0198f79a
commit f78d751027

@ -1,6 +1,35 @@
import request from '@/utils/request'
import { MessageBox } from 'element-ui'
// 采购明细
// flow/caigoumingxi-wait-list
//flow/caigoumingxi-receive-list
//flow/caigoumingxi-confirm
export function waitList(params) {
return request({
method: 'get',
url: `/api/oa/flow/caigoumingxi-wait-list`,
params
})
}
export function receiveList(params) {
return request({
method: 'get',
url: `/api/oa/flow/caigoumingxi-receive-list`,
params
})
}
export function confirmList(data) {
return request({
method: 'post',
url: `/api/oa/flow/caigoumingxi-confirm`,
data
})
}
// 已支付物资明细情况
export function getItems(params) {
return request({

@ -688,6 +688,7 @@ import payMx from "./components/payMx.vue";
import FieldExport from "./components/FieldExport.vue";
import { departmentListNoAuth } from "@/api/common";
import { deepCopy } from "@/utils";
export default {
name: "flowList",
components: {
@ -990,7 +991,8 @@ export default {
this.select.page_size = 20
this.can_pay = ''
if (e) {
if(e==99){
if (e == 99 && this.$route.params.type === 'handled') {
this.select.page = 1
this.select.page_size = 999
}
this.getFields(e);
@ -1030,6 +1032,7 @@ export default {
if (refresh) {
this.select.page = 1;
}
const res = await flowList(this.$route.params.type, this.select, false);
res.data?.data?.forEach((i) => this.contentFormatter(i));
this.list = res?.data?.data || [];

@ -0,0 +1,719 @@
<template>
<div style="padding: 20px">
<el-card shadow="always">
<template #header>
<p>支付审批明细</p>
</template>
<div>
<el-tabs v-model="activeTab">
<el-tab-pane :label="`待采购${pendingBadgeCount ? '' + pendingBadgeCount + '' : ''}`" name="pending">
<div style="margin-bottom: 10px; display: flex; justify-content: flex-start;">
<el-button
type="primary"
size="small"
icon="el-icon-edit"
@click="handleBatchPay"
>批量支付</el-button>
</div>
<el-table ref="pendingTable" :data="pendingRows" border style="width: 100%">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="申请人" width="120" align="center">
<template #default="{ row }">
{{ row.applicant }}
</template>
</el-table-column>
<el-table-column label="申请科室" width="140" align="center">
<template #default="{ row }">
{{ row.department }}
</template>
</el-table-column>
<el-table-column label="流程名称" width="160" align="center">
<template #default="{ row }">
{{ row.flowTitle }}
</template>
</el-table-column>
<el-table-column label="本次报销数量" width="160" align="center">
<template #default="{ row }">
<el-input-number
v-model="row.applyNum"
:min="0"
:controls="false"
size="small"
:class="{ 'error-input': isApplyNumInvalid(row) }"
style="width: 120px"
@change="validateApplyNum(row)"
/>
</template>
</el-table-column>
<el-table-column label="付款金额" width="160" align="center">
<template #default="{ row }">
<el-input-number v-model="row.money" :min="0" :precision="2" :controls="false" size="small" style="width: 120px" />
</template>
</el-table-column>
<el-table-column label="已报销数量" width="120" align="center">
<template #default="{ row }">
{{ row.paidNum }}
</template>
</el-table-column>
<el-table-column label="数量" width="120" align="center">
<template #default="{ row }">
{{ row.total }}
</template>
</el-table-column>
<el-table-column label="品名或服务需求" min-width="220" show-overflow-tooltip>
<template #default="{ row }">
{{ row.itemName }}
</template>
</el-table-column>
<el-table-column label="型号与规格" width="160" show-overflow-tooltip>
<template #default="{ row }">
{{ row.model }}
</template>
</el-table-column>
<el-table-column label="产地" width="120" align="center">
<template #default="{ row }">
{{ row.origin }}
</template>
</el-table-column>
<el-table-column label="单位" width="100" align="center">
<template #default="{ row }">
{{ row.unit }}
</template>
</el-table-column>
<el-table-column label="用途" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
{{ row.purpose }}
</template>
</el-table-column>
</el-table>
<div style="margin-top: 10px; display: flex; justify-content: center; color: #666;">
{{ pendingRows.length }} 条记录 · 1 / 1
</div>
</el-tab-pane>
<el-tab-pane label="采购中" name="processing">
<div style="margin-bottom: 10px; display: flex; justify-content: flex-start;">
<el-button
type="primary"
size="small"
icon="el-icon-check"
@click="handleBatchConfirm"
>批量确认</el-button>
</div>
<el-table ref="processingTable" :data="processingRows" border style="width: 100%">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="申请人" width="120" align="center">
<template #default="{ row }">
{{ row.applicant }}
</template>
</el-table-column>
<el-table-column label="申请科室" width="140" align="center">
<template #default="{ row }">
{{ row.department }}
</template>
</el-table-column>
<el-table-column label="流程名称" width="160" align="center">
<template #default="{ row }">
{{ row.flowTitle }}
</template>
</el-table-column>
<el-table-column label="已报销数量" width="120" align="center">
<template #default="{ row }">
{{ row.paidNum }}
</template>
</el-table-column>
<el-table-column label="付款金额" width="120" align="center">
<template #default="{ row }">
{{ row.money }}
</template>
</el-table-column>
<el-table-column label="数量" width="120" align="center">
<template #default="{ row }">
{{ row.total }}
</template>
</el-table-column>
<el-table-column label="品名或服务需求" min-width="220" show-overflow-tooltip>
<template #default="{ row }">
{{ row.itemName }}
</template>
</el-table-column>
<el-table-column label="型号与规格" width="160" show-overflow-tooltip>
<template #default="{ row }">
{{ row.model }}
</template>
</el-table-column>
<el-table-column label="产地" width="120" align="center">
<template #default="{ row }">
{{ row.origin }}
</template>
</el-table-column>
<el-table-column label="单位" width="100" align="center">
<template #default="{ row }">
{{ row.unit }}
</template>
</el-table-column>
<el-table-column label="用途" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
{{ row.purpose }}
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template #default="{ row }">
<el-button type="primary" size="mini" @click="confirmReceiveVue(row)">
<i class="el-icon-check icon-space"></i>确认收货
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top: 10px"
@size-change="handleProcessingSizeChange"
@current-change="handleProcessingPageChange"
:current-page="processingPage"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="processingPageSize"
layout="total, ->, prev, pager, next, sizes, jumper"
:total="processingTotal"
/>
</el-tab-pane>
<el-tab-pane label="已收货" name="received">
<div style="margin-bottom: 10px; display: flex; justify-content: flex-start;">
<el-button
type="primary"
size="small"
icon="el-icon-close"
@click="handleBatchCancel"
>批量取消</el-button>
</div>
<el-table ref="receivedTable" :data="receivedRows" border style="width: 100%">
<el-table-column type="selection" width="50"></el-table-column>
<el-table-column label="申请人" width="120" align="center">
<template #default="{ row }">
{{ row.applicant }}
</template>
</el-table-column>
<el-table-column label="申请科室" width="140" align="center">
<template #default="{ row }">
{{ row.department }}
</template>
</el-table-column>
<el-table-column label="流程名称" width="160" align="center">
<template #default="{ row }">
{{ row.flowTitle }}
</template>
</el-table-column>
<el-table-column label="已报销数量" width="120" align="center">
<template #default="{ row }">
{{ row.paidNum }}
</template>
</el-table-column>
<el-table-column label="付款金额" width="120" align="center">
<template #default="{ row }">
{{ row.money }}
</template>
</el-table-column>
<el-table-column label="数量" width="120" align="center">
<template #default="{ row }">
{{ row.total }}
</template>
</el-table-column>
<el-table-column label="品名或服务需求" min-width="220" show-overflow-tooltip>
<template #default="{ row }">
{{ row.itemName }}
</template>
</el-table-column>
<el-table-column label="型号与规格" width="160" show-overflow-tooltip>
<template #default="{ row }">
{{ row.model }}
</template>
</el-table-column>
<el-table-column label="产地" width="120" align="center">
<template #default="{ row }">
{{ row.origin }}
</template>
</el-table-column>
<el-table-column label="单位" width="100" align="center">
<template #default="{ row }">
{{ row.unit }}
</template>
</el-table-column>
<el-table-column label="用途" min-width="240" show-overflow-tooltip>
<template #default="{ row }">
{{ row.purpose }}
</template>
</el-table-column>
<el-table-column label="操作" width="120" align="center">
<template #default="{ row }">
<el-button type="primary" size="mini" @click="cancelReceiveVue(row)">
<i class="el-icon-close icon-space"></i>取消收货
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
style="margin-top: 10px"
@size-change="handleReceivedSizeChange"
@current-change="handleReceivedPageChange"
:current-page="receivedPage"
:page-sizes="[10, 20, 30, 50, 100]"
:page-size="receivedPageSize"
layout="total, ->, prev, pager, next, sizes, jumper"
:total="receivedTotal"
/>
</el-tab-pane>
</el-tabs>
</div>
</el-card>
<payMx ref="payMx" :is-show.sync="isShowPay"></payMx>
</div>
</template>
<script>
import { waitList, receiveList, confirmList,getFundLog,detailContract, } from '@/api/flow/pay'
import payMx from './components/payMx.vue'
export default {
name: 'PayList',
components: {
payMx,
},
data() {
return {
waitList: [],
receiveList: [],
confirmList: [],
activeTab: 'pending',
pendingRows: [],
isShowPay: false,
processingRows: [],
processingPage: 1,
processingPageSize: 10,
processingTotal: 0,
receivedRows: [],
receivedPage: 1,
receivedPageSize: 10,
receivedTotal: 0,
}
},
computed: {
pendingBadgeCount() {
return this.pendingRows.filter(r => r.checked).length
},
},
methods: {
switchTab(tab) { this.activeTab = tab },
isApplyNumInvalid(row) {
const applyNum = row.applyNum === null || row.applyNum === undefined ? null : Number(row.applyNum)
const paidNum = Number(row.paidNum) || 0
const total = Number(row.total) || 0
//
if (applyNum === null) {
return false
}
// 0
if (applyNum === 0) {
return true
}
// +
if (applyNum + paidNum > total) {
return true
}
return false
},
validateApplyNum(row) {
if (this.isApplyNumInvalid(row)) {
const applyNum = row.applyNum === null || row.applyNum === undefined ? null : Number(row.applyNum)
const paidNum = Number(row.paidNum) || 0
const total = Number(row.total) || 0
if (applyNum === 0) {
this.$message.warning('本次报销数量不能为0')
} else if (applyNum + paidNum > total) {
this.$message.warning('本次报销数量大于剩余可报销数量')
}
}
},
async handleBatchPay() {
const selectedRows = this.$refs.pendingTable?.selection || []
if (selectedRows.length === 0) {
this.$message.warning('请先选择需要支付的明细')
return
}
//
const invalidRows = []
selectedRows.forEach(row => {
const applyNum = Number(row.applyNum) || 0
const paidNum = Number(row.paidNum) || 0
const total = Number(row.total) || 0
// 0
if (applyNum === 0) {
invalidRows.push({
row,
reason: '本次报销数量不能为0'
})
}
// +
else if (applyNum + paidNum > total) {
invalidRows.push({
row,
reason: '本次报销数量大于剩余可报销数量'
})
}
})
//
if (invalidRows.length > 0) {
const errorMessages = invalidRows.map(item => {
const itemName = item.row.itemName || '未知项目'
return `${itemName}${item.reason}`
}).join('')
this.$message.warning(`以下数据不符合条件:${errorMessages}`)
//
this.$nextTick(() => {
//
this.$forceUpdate()
})
return
}
// baseInfo
const titleParts = selectedRows.map(r => `${r.itemName || ''}*${Number(r.applyNum) || 0}`)
const zhifutitle = `${titleParts.join(',')}${titleParts.length ? ',' : ''}`
const xiangxishuoming = zhifutitle
// ID
const contractIdSet = Array.from(new Set(
selectedRows
.map(r => r?.yibancaigou?.flow?.out_contract_id)
.filter(id => id)
))
const contractIdsStr = contractIdSet.join(',')
// ID
const contract_id = contractIdsStr
//
const amt = selectedRows.reduce((sum, r) => sum + (Number(r.money) || 0), 0)
// getFundLog
let yifujine = 0
let cishu = 0
try {
const fundRes = await getFundLog({ contract_id: contractIdsStr, show_type: 1 })
const payments = fundRes?.data || []
// @payMx.vue (591-592)
const actNumsTotal = () => payments.reduce((pre, cur) => pre + (Number(cur?.act_money) ? Number(cur.act_money) : 0), 0)
yifujine = actNumsTotal()
cishu = Array.isArray(payments) ? payments.length : 0
} catch (e) {
console.warn('getFundLog failed', e)
}
//
let xiangmuzonge = 0
let zhifucishu = 0
let liezhiqudao = ''
let contractno = ''
let guanlianliucheng = ''
try {
// @payMx.vue (579-586)
let contracts = []
if (contractIdSet.length > 0) {
contracts = await Promise.all(
contractIdSet.map(id => detailContract({ id }))
)
}
// @payMx.vue (601-606)
const sumMoney = (contracts || []).reduce((sum, c) => sum + (Number(c?.money) || 0), 0)
const sumPlanLen = (contracts || []).reduce((sum, c) => sum + ((c?.sign_plan?.length) || 0), 0)
const plansNames = Array.from(new Set((contracts || []).flatMap(c => (c?.plans || []).map(p => p?.name)).filter(Boolean))).join(',')
const contractNos = (contracts || []).map(c => c?.number).filter(Boolean).join(',')
const relatedFlows = Array.from(new Set((contracts || []).flatMap(c => (c?.contract_flow_links || []).map(l => l?.flow_id)).filter(Boolean))).join(',')
xiangmuzonge = sumMoney
zhifucishu = sumPlanLen
liezhiqudao = plansNames
contractno = contractNos
guanlianliucheng = relatedFlows
} catch (e) {
console.warn('detailContract failed', e)
}
const baseInfo = {
zhifutitle,
contract_id,
amt,
xiangxishuoming,
yifujine,
cishu,
xiangmuzonge,
zhifucishu,
liezhiqudao,
contractno,
guanlianliucheng,
wuzicaigou_items
}
//
const wuzicaigou_items = selectedRows.map(r => ({
flow_id: r?.yibancaigou?.flow?.id,
contract_id: r?.yibancaigou?.flow?.out_contract_id,
wuzicaigou_id: r?.yibancaigou?.id,
wuzicaigou_item_id: r?.id,
name: r?.itemName,
num: Number(r?.applyNum) || 0,
total: Number(r?.num) || 0,
apply_money: Number(r?.money) || 0,
money: Number(r?.money) || 0,
is_receive: 0
}))
// 使
baseInfo.wuzicaigou_items = wuzicaigou_items
console.log(baseInfo, 'baseInfo')
const url = `/flow/create?&module_name=oa&isSinglePage=1&module_id=75&default_json=${window.encodeURIComponent(JSON.stringify(baseInfo))}`
this.$router.push(url)
// this.$message.success('')
},
handleBatchConfirm() {
const selectedRows = this.$refs.processingTable?.selection || []
if (selectedRows.length === 0) {
this.$message.warning('请先选择需要确认收货的数据')
return
}
this.$confirm(`确定要确认收货吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
const ids = selectedRows.map(row => row.id).join(',')
this.confirmReceive(ids)
})
.catch(() => {})
},
confirmReceiveVue(row) {
this.$confirm(`确定要确认收货吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
this.confirmReceive(row.id)
})
.catch(() => {})
},
async confirmReceive(id) {
try {
await confirmList({
ids: id,
is_receive: 1,
})
this.$message.success('确认收货成功')
//
await this.getProcessingList()
} catch (err) {
console.error('确认收货失败:', err)
this.$message.error('确认收货失败')
}
},
handleBatchCancel() {
const selectedRows = this.$refs.receivedTable?.selection || []
if (selectedRows.length === 0) {
this.$message.warning('请先选择需要取消收货的数据')
return
}
this.$confirm(`确定要取消收货吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
const ids = selectedRows.map(row => row.id).join(',')
this.cancelReceive(ids)
})
.catch(() => {})
},
cancelReceiveVue(row) {
this.$confirm('确定要取消收货吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
.then(() => {
this.cancelReceive(row.id)
})
.catch(() => {})
},
async cancelReceive(id) {
try {
await confirmList({
ids: id,
is_receive: 0,
})
this.$message.success('取消收货成功')
//
await this.getReceivedList()
} catch (err) {
console.error('取消收货失败:', err)
this.$message.error('取消收货失败')
}
},
async getWaitList() {
try {
const res = await waitList({
page: 1,
page_size: 999,
})
this.waitList = res.data || []
//
this.pendingRows = (res.data || []).map(item => ({
...item, // 便使
checked: false,
applicant: item.yibancaigou?.flow?.creator?.name || '',
department: item.yibancaigou?.flow?.creator_department?.name || '',
flowTitle: item.yibancaigou?.flow?.title || '',
paidNum: item.fund_log_wuzicaigou_items_sum_num || 0,
total: item.num || 0,
itemName: item.pinminghuofuwuxuqiu || '',
model: item.xinghao || '',
origin: item.chandi || '',
unit: item.danwei || '',
purpose: item.yongtu || '',
applyNum: null, //
money: null, //
}))
} catch (err) {
console.error('获取待采购列表失败:', err)
this.$message.error('获取待采购列表失败')
}
},
async getProcessingList() {
try {
const res = await receiveList({
page: this.processingPage,
page_size: this.processingPageSize,
is_receive: 0,
})
this.processingTotal = res.total || 0
//
this.processingRows = (res.data || []).map(item => ({
...item,
applicant: item.yibancaigou?.flow?.creator?.name || '',
department: item.yibancaigou?.flow?.creator_department?.name || '',
flowTitle: item.yibancaigou?.flow?.title || '',
paidNum: item.num || 0,
money: item.money || 0,
total: item.caigoumingxi?.num || 0,
itemName: item.caigoumingxi?.pinminghuofuwuxuqiu || '',
model: item.caigoumingxi?.xinghao || '',
origin: item.caigoumingxi?.chandi || '',
unit: item.caigoumingxi?.danwei || '',
purpose: item.caigoumingxi?.yongtu || '',
}))
} catch (err) {
console.error('获取采购中列表失败:', err)
this.$message.error('获取采购中列表失败')
}
},
async getReceivedList() {
try {
const res = await receiveList({
page: this.receivedPage,
page_size: this.receivedPageSize,
is_receive: 1,
})
this.receivedTotal = res.total || 0
//
this.receivedRows = (res.data || []).map(item => ({
...item,
applicant: item.yibancaigou?.flow?.creator?.name || '',
department: item.yibancaigou?.flow?.creator_department?.name || '',
flowTitle: item.yibancaigou?.flow?.title || '',
paidNum: item.num || 0,
money: item.money || 0,
total: item.caigoumingxi?.num || 0,
itemName: item.caigoumingxi?.pinminghuofuwuxuqiu || '',
model: item.caigoumingxi?.xinghao || '',
origin: item.caigoumingxi?.chandi || '',
unit: item.caigoumingxi?.danwei || '',
purpose: item.caigoumingxi?.yongtu || '',
}))
} catch (err) {
console.error('获取已收货列表失败:', err)
this.$message.error('获取已收货列表失败')
}
},
handleProcessingSizeChange(val) {
this.processingPageSize = val
this.processingPage = 1
this.getProcessingList()
},
handleProcessingPageChange(val) {
this.processingPage = val
this.getProcessingList()
},
handleReceivedSizeChange(val) {
this.receivedPageSize = val
this.receivedPage = 1
this.getReceivedList()
},
handleReceivedPageChange(val) {
this.receivedPage = val
this.getReceivedList()
},
},
watch: {
activeTab(newTab, oldTab) {
//
if (newTab === 'processing') {
this.processingPage = 1
this.processingPageSize = 10
} else if (newTab === 'received') {
this.receivedPage = 1
this.receivedPageSize = 10
}
// 使 nextTick
this.$nextTick(() => {
this.$nextTick(() => {
if (this.$refs.pendingTable) {
this.$refs.pendingTable.clearSelection()
}
if (this.$refs.processingTable) {
this.$refs.processingTable.clearSelection()
}
if (this.$refs.receivedTable) {
this.$refs.receivedTable.clearSelection()
}
})
})
//
if (newTab === 'processing') {
this.getProcessingList()
} else if (newTab === 'received') {
this.getReceivedList()
} else if (newTab === 'pending') {
//
this.getWaitList()
}
},
},
mounted() {
this.getWaitList()
},
}
</script>
<style scoped>
.icon-space { margin-right: 6px; }
::v-deep .error-input .el-input__inner {
border-color: #f56c6c !important;
}
::v-deep .error-input:hover .el-input__inner {
border-color: #f56c6c !important;
}
::v-deep .error-input .el-input__inner:focus {
border-color: #f56c6c !important;
}
</style>
Loading…
Cancel
Save