Files
shanxia2.0/web-admin/src/views/pl/freight/components/ApprovalPendingPanel.vue
T
2026-07-05 10:07:24 +08:00

339 lines
10 KiB
Vue

<template>
<div class="freight-tab-panel">
<div class="filter-block">
<el-form :model="searchForm" class="filter-form" @submit.prevent>
<div class="filter-grid">
<el-form-item label="结算单号">
<el-input
v-model="searchForm.q"
placeholder="搜索结算单号"
clearable
@keyup.enter="handleSearch"
/>
</el-form-item>
</div>
<div class="filter-actions">
<div class="filter-actions-end">
<el-button class="btn-ghost" :loading="loading" @click="handleReset">
<el-icon><RefreshLeft /></el-icon>重置
</el-button>
<el-button class="btn-main" type="primary" :loading="loading" @click="handleSearch">
<el-icon><Search /></el-icon>搜索
</el-button>
</div>
</div>
</el-form>
</div>
<div class="list-toolbar">
<div class="list-toolbar-row">
<div class="list-toolbar-selected">
已选 <span class="sel-num">{{ selectedRows.length }}</span> 项
</div>
<ListQuickSearchInput
v-model="quickSearchKeyword"
placeholder="结算单号"
@search="handleQuickSearch"
@clear="handleQuickSearchClear"
/>
<el-button
class="btn-main"
type="primary"
:disabled="
selectedRows.length !== 1 ||
!selectedRows[0]?.canApprove ||
!selectedRows[0]?.currentApprovalId
"
@click="handleToolbarApprove"
>
<el-icon><CircleCheck /></el-icon>通过
</el-button>
<el-button
class="btn-ghost"
type="danger"
:disabled="
selectedRows.length !== 1 ||
!selectedRows[0]?.canApprove ||
!selectedRows[0]?.currentApprovalId
"
@click="handleToolbarReject"
>
<el-icon><CircleClose /></el-icon>驳回
</el-button>
<ListToolbarEndActions
:loading="loading"
:column-defs="listColumnDefs"
v-model:visible-columns="visibleColumns"
v-model:check-all="checkAll"
:is-indeterminate="isIndeterminate"
@refresh="fetchData"
@reset-columns="resetToDefault"
/>
</div>
</div>
<div class="list-body list-wrap">
<el-table
class="list-table"
:data="settlementList"
v-loading="loading"
stripe
row-key="id"
@selection-change="handleSelectionChange"
@row-dblclick="handleView"
>
<el-table-column type="selection" min-width="55" align="center" header-align="center" />
<el-table-column
type="index"
label="序号"
min-width="68"
align="center"
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
<el-table-column prop="settlementNo" label="结算单号" min-width="180" show-overflow-tooltip>
<template #default="{ row }">
<el-link type="primary" @click="handleView(row)">{{ row.settlementNo }}</el-link>
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('totalFreight')"
prop="totalFreight"
label="运费合计(元)"
min-width="130"
align="right"
>
<template #default="{ row }">{{ formatAmount(row.totalFreight) }}</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('planCount')"
prop="planCount"
label="关联计划数"
min-width="110"
align="center"
/>
<el-table-column
v-if="isColumnVisible('licensePlateSummary')"
prop="licensePlateSummary"
label="车牌摘要"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
v-if="isColumnVisible('currentApproverName')"
prop="currentApproverName"
label="当前审批人"
min-width="110"
align="center"
>
<template #default="{ row }">{{ row.currentApproverName || '—' }}</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('submitterName')"
prop="submitterName"
label="提交人"
min-width="100"
align="center"
>
<template #default="{ row }">{{ row.submitterName || '—' }}</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('createTime')"
prop="createTime"
label="提交时间"
min-width="160"
align="center"
>
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
</el-table-column>
</el-table>
<div class="pagination list-pager-bar">
<el-pagination
v-model:current-page="pagination.page"
v-model:page-size="pagination.pageSize"
:total="pagination.total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>
</div>
<PlFreightSettlementViewDialog ref="viewDialogRef">
<template v-if="viewingRow" #hero-actions>
<el-button
v-if="viewingRow.canApprove && viewingRow.currentApprovalId"
class="btn-main"
type="primary"
@click="handleApprove(viewingRow)"
>
<el-icon><CircleCheck /></el-icon>通过
</el-button>
<el-button
v-if="viewingRow.canApprove && viewingRow.currentApprovalId"
class="btn-ghost"
type="danger"
@click="handleReject(viewingRow)"
>
<el-icon><CircleClose /></el-icon>驳回
</el-button>
</template>
</PlFreightSettlementViewDialog>
<PlFreightApprovalDialog ref="approvalDialogRef" @success="handleMutationSuccess" />
</div>
</template>
<script setup lang="ts">
import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue'
import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue'
import { usePersistedListColumns } from '@/composables/usePersistedListColumns'
import { CircleCheck, CircleClose, RefreshLeft, Search } from '@element-plus/icons-vue'
import { freightApi, type FreightSettlement } from '@/api/pl'
import PlFreightApprovalDialog from '@/components/business/pl-freight-approval-dialog/index.vue'
import PlFreightSettlementViewDialog from '@/components/business/pl-freight-settlement-view-dialog/index.vue'
import { formatMoneyDisplay } from '@/utils/money-format'
import { formatDateTime } from '@/utils/format'
const listColumnDefs = [
{ prop: 'totalFreight', label: '运费合计(元)' },
{ prop: 'planCount', label: '关联计划数' },
{ prop: 'licensePlateSummary', label: '车牌摘要' },
{ prop: 'currentApproverName', label: '当前审批人' },
{ prop: 'submitterName', label: '提交人' },
{ prop: 'createTime', label: '提交时间' }
]
const {
visibleColumns,
checkAll,
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('pl-freight-approval-pending-columns', listColumnDefs)
const loading = ref(false)
const settlementList = ref<FreightSettlement[]>([])
const selectedRows = ref<FreightSettlement[]>([])
const viewingRow = ref<FreightSettlement | null>(null)
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
const approvalDialogRef = ref<InstanceType<typeof PlFreightApprovalDialog>>()
const quickSearchKeyword = ref('')
const searchForm = reactive({ q: '' })
const pagination = reactive({
page: 1,
pageSize: 20,
total: 0
})
function formatAmount(amount: number) {
if (!amount) return '¥0.00'
return formatMoneyDisplay(amount)
}
function handleSelectionChange(selection: FreightSettlement[]) {
selectedRows.value = selection
}
async function fetchData() {
loading.value = true
try {
const res = await freightApi.list({
tab: 'approval_pending',
q: quickSearchKeyword.value.trim() || searchForm.q || undefined,
page: pagination.page,
pageSize: pagination.pageSize
})
const data = res.data ?? res
settlementList.value = data.list || []
pagination.total = data.total || 0
} finally {
loading.value = false
}
}
function handleSearch() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleReset() {
searchForm.q = ''
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handleQuickSearch() {
pagination.page = 1
fetchData()
}
function handleQuickSearchClear() {
quickSearchKeyword.value = ''
pagination.page = 1
fetchData()
}
function handlePageChange() {
fetchData()
}
function handleSizeChange() {
pagination.page = 1
fetchData()
}
function handleView(row: FreightSettlement) {
viewingRow.value = row
viewDialogRef.value?.open(row.id)
}
function handleApprove(row: FreightSettlement) {
if (!row.currentApprovalId) return
approvalDialogRef.value?.openApprove(row.id, row.currentApprovalId)
}
function handleReject(row: FreightSettlement) {
if (!row.currentApprovalId) return
approvalDialogRef.value?.openReject(row.id, row.currentApprovalId)
}
function syncViewingRowFromList() {
if (!viewingRow.value?.id) return
const updated = settlementList.value.find(row => row.id === viewingRow.value!.id)
if (updated) viewingRow.value = updated
}
async function handleMutationSuccess() {
await fetchData()
syncViewingRowFromList()
await viewDialogRef.value?.reload()
}
function handleToolbarApprove() {
if (selectedRows.value.length !== 1) return
handleApprove(selectedRows.value[0])
}
function handleToolbarReject() {
if (selectedRows.value.length !== 1) return
handleReject(selectedRows.value[0])
}
function refresh() {
fetchData()
}
onMounted(() => {
fetchData()
})
defineExpose({ refresh })
</script>