feat(pl): 运费结算审批分流与车辆挂车号等 PL 能力完善
拆分运费审批/出款页面,补充结算发票附件与菜单 SQL,并同步前后端模型与车辆相关展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
66d5a8714b
commit
98aec80d71
37 files changed
+1362
-195
No files matched your search
@@ -0,0 +1,41 @@
|
||||
<!-- 运费结算审批 -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">运费结算审批</h2>
|
||||
</div>
|
||||
|
||||
<div class="panel-tabs">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="待审批" name="pending">
|
||||
<ApprovalPendingPanel ref="pendingRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="已审批" name="done">
|
||||
<ApprovalDonePanel ref="doneRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import ApprovalPendingPanel from '../freight/components/ApprovalPendingPanel.vue'
|
||||
import ApprovalDonePanel from '../freight/components/ApprovalDonePanel.vue'
|
||||
|
||||
type ApprovalTab = 'pending' | 'done'
|
||||
|
||||
const activeTab = ref<ApprovalTab>('pending')
|
||||
const pendingRef = ref<InstanceType<typeof ApprovalPendingPanel>>()
|
||||
const doneRef = ref<InstanceType<typeof ApprovalDonePanel>>()
|
||||
|
||||
function refreshTab(name: ApprovalTab) {
|
||||
if (name === 'pending') pendingRef.value?.refresh()
|
||||
else doneRef.value?.refresh()
|
||||
}
|
||||
|
||||
function handleTabChange(name: string | number) {
|
||||
refreshTab(String(name) as ApprovalTab)
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!-- 运费财务发放 -->
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<section class="panel">
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">运费财务发放</h2>
|
||||
</div>
|
||||
|
||||
<div class="panel-tabs">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="待发放" name="disburse">
|
||||
<DisbursePanel ref="disburseRef" @disbursed="handleDisbursed" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="已发放" name="settled">
|
||||
<SettledPanel ref="settledRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import DisbursePanel from '../freight/components/DisbursePanel.vue'
|
||||
import SettledPanel from '../freight/components/SettledPanel.vue'
|
||||
|
||||
type DisburseTab = 'disburse' | 'settled'
|
||||
|
||||
const activeTab = ref<DisburseTab>('disburse')
|
||||
const disburseRef = ref<InstanceType<typeof DisbursePanel>>()
|
||||
const settledRef = ref<InstanceType<typeof SettledPanel>>()
|
||||
|
||||
function refreshTab(name: DisburseTab) {
|
||||
if (name === 'disburse') disburseRef.value?.refresh()
|
||||
else settledRef.value?.refresh()
|
||||
}
|
||||
|
||||
function handleTabChange(name: string | number) {
|
||||
refreshTab(String(name) as DisburseTab)
|
||||
}
|
||||
|
||||
function handleDisbursed() {
|
||||
nextTick(() => {
|
||||
settledRef.value?.refresh()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,176 @@
|
||||
<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>
|
||||
<el-form-item label="结算状态">
|
||||
<el-select v-model="searchForm.settlementStatus" placeholder="全部" clearable>
|
||||
<el-option label="待发放" value="approved" />
|
||||
<el-option label="已发放" value="confirmed" />
|
||||
</el-select>
|
||||
</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">0</span> 项</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table class="list-table" :data="settlementList" v-loading="loading" stripe row-key="id">
|
||||
<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 prop="totalFreight" label="运费合计(元)" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.totalFreight) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="planCount" label="关联计划数" min-width="110" align="center" />
|
||||
<el-table-column prop="licensePlateSummary" label="车牌摘要" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="settlementStatus" label="结算状态" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getSettlementStatusTagType(row.settlementStatus)" size="small">
|
||||
{{ getSettlementStatusLabel(row.settlementStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="approvalFinishedTime" label="审批完成时间" min-width="160" align="center">
|
||||
<template #default="{ row }">{{
|
||||
row.approvalFinishedTime ? formatDateTime(row.approvalFinishedTime) : '—'
|
||||
}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="submitterName" label="提交人" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.submitterName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="120" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
</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" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RefreshLeft, Search, View } from '@element-plus/icons-vue'
|
||||
import { freightApi, type FreightSettlement } from '@/api/pl'
|
||||
import PlFreightSettlementViewDialog from '@/components/business/pl-freight-settlement-view-dialog/index.vue'
|
||||
import {
|
||||
getSettlementStatusLabel,
|
||||
getSettlementStatusTagType
|
||||
} from '@/views/pl/freight/freight-status'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
|
||||
const loading = ref(false)
|
||||
const settlementList = ref<FreightSettlement[]>([])
|
||||
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
|
||||
|
||||
const searchForm = reactive({
|
||||
q: '',
|
||||
settlementStatus: ''
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0
|
||||
})
|
||||
|
||||
function formatAmount(amount: number) {
|
||||
if (!amount) return '¥0.00'
|
||||
return formatMoneyDisplay(amount)
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await freightApi.list({
|
||||
tab: 'approval_done',
|
||||
q: searchForm.q || undefined,
|
||||
settlementStatus: searchForm.settlementStatus || 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() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.q = ''
|
||||
searchForm.settlementStatus = ''
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleView(row: FreightSettlement) {
|
||||
viewDialogRef.value?.open(row.id)
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
defineExpose({ refresh })
|
||||
</script>
|
||||
@@ -0,0 +1,184 @@
|
||||
<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">0</span> 项</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="list-body list-wrap">
|
||||
<el-table class="list-table" :data="settlementList" v-loading="loading" stripe row-key="id">
|
||||
<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 prop="totalFreight" label="运费合计(元)" min-width="130" align="right">
|
||||
<template #default="{ row }">{{ formatAmount(row.totalFreight) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="planCount" label="关联计划数" min-width="110" align="center" />
|
||||
<el-table-column prop="licensePlateSummary" label="车牌摘要" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column prop="currentApproverName" label="当前审批人" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.currentApproverName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="submitterName" label="提交人" min-width="100" align="center">
|
||||
<template #default="{ row }">{{ row.submitterName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="提交时间" min-width="160" align="center">
|
||||
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="260" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleView(row)">
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.canApprove && row.currentApprovalId"
|
||||
link
|
||||
type="success"
|
||||
@click="handleApprove(row)"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>通过
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.canApprove && row.currentApprovalId"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleReject(row)"
|
||||
>
|
||||
<el-icon><CircleClose /></el-icon>驳回
|
||||
</el-button>
|
||||
</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" />
|
||||
<PlFreightApprovalDialog ref="approvalDialogRef" @success="fetchData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { CircleCheck, CircleClose, RefreshLeft, Search, View } 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 loading = ref(false)
|
||||
const settlementList = ref<FreightSettlement[]>([])
|
||||
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
|
||||
const approvalDialogRef = ref<InstanceType<typeof PlFreightApprovalDialog>>()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await freightApi.list({
|
||||
tab: 'approval_pending',
|
||||
q: 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() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.q = ''
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
function handleView(row: FreightSettlement) {
|
||||
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 refresh() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
})
|
||||
|
||||
defineExpose({ refresh })
|
||||
</script>
|
||||
@@ -3,6 +3,16 @@
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="运输公司">
|
||||
<el-select v-model="searchForm.carrierId" placeholder="全部" clearable filterable>
|
||||
<el-option
|
||||
v-for="c in carrierList"
|
||||
:key="c.id"
|
||||
:label="c.carrierName"
|
||||
:value="c.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="结算单号">
|
||||
<el-input
|
||||
v-model="searchForm.q"
|
||||
@@ -15,6 +25,8 @@
|
||||
<el-select v-model="searchForm.settlementStatus" placeholder="全部" clearable>
|
||||
<el-option label="草稿" value="pending" />
|
||||
<el-option label="审批中" value="approving" />
|
||||
<el-option label="待发放" value="approved" />
|
||||
<el-option label="已发放" value="confirmed" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
@@ -56,6 +68,16 @@
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="disburseAmount" label="发放金额(元)" min-width="130" align="right">
|
||||
<template #default="{ row }">{{
|
||||
row.disburseAmount ? formatAmount(row.disburseAmount) : '—'
|
||||
}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="disburseTime" label="发放时间" min-width="160" align="center">
|
||||
<template #default="{ row }">{{
|
||||
row.disburseTime ? formatDateTime(row.disburseTime) : '—'
|
||||
}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="currentApproverName" label="当前审批人" min-width="110" align="center">
|
||||
<template #default="{ row }">{{ row.currentApproverName || '—' }}</template>
|
||||
</el-table-column>
|
||||
@@ -78,6 +100,14 @@
|
||||
>
|
||||
<el-icon><Edit /></el-icon>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.settlementStatus === 'approved'"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleEdit(row)"
|
||||
>
|
||||
<el-icon><Edit /></el-icon>发票信息
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.settlementStatus === 'pending'"
|
||||
link
|
||||
@@ -102,22 +132,6 @@
|
||||
>
|
||||
<el-icon><RefreshLeft /></el-icon>撤回
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.canApprove && row.currentApprovalId"
|
||||
link
|
||||
type="success"
|
||||
@click="handleApprove(row)"
|
||||
>
|
||||
<el-icon><CircleCheck /></el-icon>审批
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.canApprove && row.currentApprovalId"
|
||||
link
|
||||
type="danger"
|
||||
@click="handleReject(row)"
|
||||
>
|
||||
<el-icon><CircleClose /></el-icon>驳回
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -143,8 +157,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
CircleCheck,
|
||||
CircleClose,
|
||||
Delete,
|
||||
Edit,
|
||||
Promotion,
|
||||
@@ -152,7 +164,7 @@ import {
|
||||
Search,
|
||||
View
|
||||
} from '@element-plus/icons-vue'
|
||||
import { freightApi, type FreightSettlement } from '@/api/pl'
|
||||
import { carrierApi, freightApi, type Carrier, type FreightSettlement } from '@/api/pl'
|
||||
import PlFreightApprovalDialog from '@/components/business/pl-freight-approval-dialog/index.vue'
|
||||
import PlFreightSettlementFormDialog from '@/components/business/pl-freight-settlement-form-dialog/index.vue'
|
||||
import PlFreightSettlementViewDialog from '@/components/business/pl-freight-settlement-view-dialog/index.vue'
|
||||
@@ -164,12 +176,14 @@ import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
|
||||
const loading = ref(false)
|
||||
const carrierList = ref<Carrier[]>([])
|
||||
const settlementList = ref<FreightSettlement[]>([])
|
||||
const viewDialogRef = ref<InstanceType<typeof PlFreightSettlementViewDialog>>()
|
||||
const formDialogRef = ref<InstanceType<typeof PlFreightSettlementFormDialog>>()
|
||||
const approvalDialogRef = ref<InstanceType<typeof PlFreightApprovalDialog>>()
|
||||
|
||||
const searchForm = reactive({
|
||||
carrierId: '',
|
||||
q: '',
|
||||
settlementStatus: ''
|
||||
})
|
||||
@@ -190,6 +204,7 @@ async function fetchData() {
|
||||
try {
|
||||
const res = await freightApi.list({
|
||||
tab: 'bills',
|
||||
carrierId: searchForm.carrierId || undefined,
|
||||
q: searchForm.q || undefined,
|
||||
settlementStatus: searchForm.settlementStatus || undefined,
|
||||
page: pagination.page,
|
||||
@@ -209,12 +224,22 @@ function handleSearch() {
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.carrierId = ''
|
||||
searchForm.q = ''
|
||||
searchForm.settlementStatus = ''
|
||||
pagination.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
async function loadCarrierList() {
|
||||
try {
|
||||
const res = await carrierApi.list({ status: '1' })
|
||||
carrierList.value = (res.data ?? res) || []
|
||||
} catch {
|
||||
carrierList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
@@ -236,16 +261,6 @@ function handleSubmit(row: FreightSettlement) {
|
||||
approvalDialogRef.value?.openSubmit(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)
|
||||
}
|
||||
|
||||
async function handleWithdraw(row: FreightSettlement) {
|
||||
await ElMessageBox.confirm('确定撤回该结算单的审批吗?', '提示', { type: 'warning' })
|
||||
await freightApi.withdrawApproval(row.id)
|
||||
@@ -267,6 +282,7 @@ function refresh() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCarrierList()
|
||||
fetchData()
|
||||
})
|
||||
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
<div class="filter-block">
|
||||
<el-form :model="searchForm" class="filter-form" @submit.prevent>
|
||||
<div class="filter-grid">
|
||||
<el-form-item label="运输公司">
|
||||
<el-select v-model="searchForm.carrierId" placeholder="全部" clearable filterable>
|
||||
<el-option
|
||||
v-for="c in carrierList"
|
||||
:key="c.id"
|
||||
:label="c.carrierName"
|
||||
:value="c.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="计划编号">
|
||||
<el-input
|
||||
v-model="searchForm.q"
|
||||
@@ -114,7 +124,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DocumentAdd, RefreshLeft, Search, View } from '@element-plus/icons-vue'
|
||||
import { freightApi, type UnsettledPlanItem } from '@/api/pl'
|
||||
import { carrierApi, freightApi, type Carrier, type UnsettledPlanItem } from '@/api/pl'
|
||||
import PlPlanViewDialog from '@/components/business/pl-plan-view-dialog/index.vue'
|
||||
import { getPlanTypeLabel, getPlanTypeTagType } from '@/views/pl/freight/freight-status'
|
||||
import { formatMoneyDisplay } from '@/utils/money-format'
|
||||
@@ -124,12 +134,14 @@ const emit = defineEmits<{ created: [settlementId: string] }>()
|
||||
|
||||
const loading = ref(false)
|
||||
const createLoading = ref(false)
|
||||
const carrierList = ref<Carrier[]>([])
|
||||
const planList = ref<UnsettledPlanItem[]>([])
|
||||
const selectedRows = ref<UnsettledPlanItem[]>([])
|
||||
const planViewVisible = ref(false)
|
||||
const viewPlanId = ref('')
|
||||
|
||||
const searchForm = reactive({
|
||||
carrierId: '',
|
||||
q: '',
|
||||
planType: '',
|
||||
licensePlate: ''
|
||||
@@ -150,6 +162,7 @@ async function fetchData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await freightApi.listUnsettledPlans({
|
||||
carrierId: searchForm.carrierId || undefined,
|
||||
q: searchForm.q || undefined,
|
||||
planType: searchForm.planType || undefined,
|
||||
licensePlate: searchForm.licensePlate || undefined,
|
||||
@@ -170,6 +183,7 @@ function handleSearch() {
|
||||
}
|
||||
|
||||
function handleReset() {
|
||||
searchForm.carrierId = ''
|
||||
searchForm.q = ''
|
||||
searchForm.planType = ''
|
||||
searchForm.licensePlate = ''
|
||||
@@ -177,6 +191,15 @@ function handleReset() {
|
||||
fetchData()
|
||||
}
|
||||
|
||||
async function loadCarrierList() {
|
||||
try {
|
||||
const res = await carrierApi.list({ status: '1' })
|
||||
carrierList.value = (res.data ?? res) || []
|
||||
} catch {
|
||||
carrierList.value = []
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange() {
|
||||
fetchData()
|
||||
}
|
||||
@@ -222,6 +245,7 @@ function refresh() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadCarrierList()
|
||||
fetchData()
|
||||
})
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ export function getSettlementStatusLabel(status: string) {
|
||||
pending: '草稿',
|
||||
approving: '审批中',
|
||||
approved: '待发放',
|
||||
confirmed: '已结算'
|
||||
confirmed: '已发放'
|
||||
}
|
||||
return map[status] || status
|
||||
}
|
||||
|
||||
@@ -14,12 +14,6 @@
|
||||
<el-tab-pane label="结算单" name="bills">
|
||||
<SettlementBillPanel ref="billsRef" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="待财务发放" name="disburse">
|
||||
<DisbursePanel ref="disburseRef" @disbursed="handleDisbursed" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="已结算" name="settled">
|
||||
<SettledPanel ref="settledRef" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</section>
|
||||
@@ -29,22 +23,16 @@
|
||||
<script setup lang="ts">
|
||||
import UnsettledPlanPanel from './components/UnsettledPlanPanel.vue'
|
||||
import SettlementBillPanel from './components/SettlementBillPanel.vue'
|
||||
import DisbursePanel from './components/DisbursePanel.vue'
|
||||
import SettledPanel from './components/SettledPanel.vue'
|
||||
|
||||
type FreightTab = 'unsettled' | 'bills' | 'disburse' | 'settled'
|
||||
type FreightTab = 'unsettled' | 'bills'
|
||||
|
||||
const activeTab = ref<FreightTab>('unsettled')
|
||||
const unsettledRef = ref<InstanceType<typeof UnsettledPlanPanel>>()
|
||||
const billsRef = ref<InstanceType<typeof SettlementBillPanel>>()
|
||||
const disburseRef = ref<InstanceType<typeof DisbursePanel>>()
|
||||
const settledRef = ref<InstanceType<typeof SettledPanel>>()
|
||||
|
||||
function refreshTab(name: FreightTab) {
|
||||
if (name === 'unsettled') unsettledRef.value?.refresh()
|
||||
else if (name === 'bills') billsRef.value?.refresh()
|
||||
else if (name === 'disburse') disburseRef.value?.refresh()
|
||||
else if (name === 'settled') settledRef.value?.refresh()
|
||||
else billsRef.value?.refresh()
|
||||
}
|
||||
|
||||
function handleTabChange(name: string | number) {
|
||||
@@ -57,8 +45,4 @@ function handleSettlementCreated() {
|
||||
billsRef.value?.refresh()
|
||||
})
|
||||
}
|
||||
|
||||
function handleDisbursed() {
|
||||
settledRef.value?.refresh()
|
||||
}
|
||||
</script>
|
||||
@@ -133,11 +133,6 @@
|
||||
<PlPlanListProductCell :products="row.products" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划附件" min-width="400">
|
||||
<template #default="{ row }">
|
||||
<PlPlanListCertCell :products="row.products" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="archiveStatus" label="归档状态" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="getPlPlanArchiveStatusTagType(row.archiveStatus)" size="small">
|
||||
@@ -235,7 +230,6 @@ import {
|
||||
import { planApi, type Plan } from '@/api/pl'
|
||||
import PlPlanArchiveDialog from '@/components/business/pl-plan-archive-dialog/index.vue'
|
||||
import PlPlanFormDialog from '@/components/business/pl-plan-form-dialog/index.vue'
|
||||
import PlPlanListCertCell from '@/components/business/pl-plan-list-cert-cell/index.vue'
|
||||
import PlPlanListProductCell from '@/components/business/pl-plan-list-product-cell/index.vue'
|
||||
import PlPlanViewDialog from '@/components/business/pl-plan-view-dialog/index.vue'
|
||||
import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
|
||||
|
||||
@@ -76,6 +76,10 @@
|
||||
</div>
|
||||
|
||||
<dl class="vehicle-card-kv">
|
||||
<div class="vehicle-card-kv-item">
|
||||
<dt>挂车号</dt>
|
||||
<dd>{{ row.trailerNo || '—' }}</dd>
|
||||
</div>
|
||||
<div class="vehicle-card-kv-item">
|
||||
<dt>载重(吨)</dt>
|
||||
<dd>{{ row.loadCapacity || '—' }}</dd>
|
||||
@@ -119,6 +123,9 @@
|
||||
<el-form-item label="车牌号" prop="licensePlate">
|
||||
<el-input v-model="formData.licensePlate" placeholder="请输入车牌号" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂车号" prop="trailerNo">
|
||||
<el-input v-model="formData.trailerNo" placeholder="请输入挂车号(选填)" maxlength="20" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="载重(吨)" prop="loadCapacity">
|
||||
<el-input-number
|
||||
v-model="formData.loadCapacity"
|
||||
@@ -315,6 +322,7 @@ const searchForm = reactive({
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
loadCapacity: null as number | null,
|
||||
vehicleStatus: 'available',
|
||||
remark: ''
|
||||
@@ -349,6 +357,7 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
title: '车辆信息',
|
||||
fields: [
|
||||
{ label: '车牌号', value: d.licensePlate },
|
||||
{ label: '挂车号', value: d.trailerNo || '—' },
|
||||
{ label: '车辆类型', value: '自有', tagType: 'success' },
|
||||
{ label: '载重(吨)', value: d.loadCapacity ?? '—' },
|
||||
{
|
||||
@@ -553,6 +562,7 @@ function handleAdd() {
|
||||
Object.assign(formData, {
|
||||
id: '',
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
loadCapacity: null,
|
||||
vehicleStatus: 'available',
|
||||
remark: ''
|
||||
@@ -596,6 +606,7 @@ async function handleEdit(row: Vehicle) {
|
||||
Object.assign(formData, {
|
||||
id: detail.id,
|
||||
licensePlate: detail.licensePlate,
|
||||
trailerNo: detail.trailerNo || '',
|
||||
loadCapacity: detail.loadCapacity ?? null,
|
||||
vehicleStatus: detail.vehicleStatus,
|
||||
remark: detail.remark
|
||||
@@ -634,6 +645,7 @@ async function handleSubmit() {
|
||||
try {
|
||||
const payload = {
|
||||
licensePlate: formData.licensePlate,
|
||||
trailerNo: formData.trailerNo || undefined,
|
||||
vehicleType: VEHICLE_TYPE_SELF,
|
||||
loadCapacity: formData.loadCapacity,
|
||||
vehicleStatus: formData.vehicleStatus,
|
||||
|
||||
@@ -97,6 +97,10 @@
|
||||
</div>
|
||||
|
||||
<dl class="vehicle-card-kv">
|
||||
<div class="vehicle-card-kv-item">
|
||||
<dt>挂车号</dt>
|
||||
<dd>{{ row.trailerNo || '—' }}</dd>
|
||||
</div>
|
||||
<div class="vehicle-card-kv-item">
|
||||
<dt>载重(吨)</dt>
|
||||
<dd>{{ row.loadCapacity || '—' }}</dd>
|
||||
@@ -145,6 +149,9 @@
|
||||
<el-form-item label="车牌号" prop="licensePlate">
|
||||
<el-input v-model="formData.licensePlate" placeholder="请输入车牌号" maxlength="20" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂车号" prop="trailerNo">
|
||||
<el-input v-model="formData.trailerNo" placeholder="请输入挂车号(选填)" maxlength="20" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="车辆类型" prop="vehicleType">
|
||||
<el-select v-model="formData.vehicleType" placeholder="请选择">
|
||||
<el-option label="自有" value="self" />
|
||||
@@ -358,6 +365,7 @@ const searchForm = reactive({
|
||||
const formData = reactive({
|
||||
id: '',
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
vehicleType: 'self',
|
||||
carrierId: '',
|
||||
carrierName: '',
|
||||
@@ -416,6 +424,7 @@ const viewSections = computed<ViewSection[]>(() => {
|
||||
title: '车辆信息',
|
||||
fields: [
|
||||
{ label: '车牌号', value: d.licensePlate },
|
||||
{ label: '挂车号', value: d.trailerNo || '—' },
|
||||
{
|
||||
label: '车辆类型',
|
||||
value: getVehicleTypeLabel(d.vehicleType),
|
||||
@@ -651,6 +660,7 @@ function handleAdd() {
|
||||
Object.assign(formData, {
|
||||
id: '',
|
||||
licensePlate: '',
|
||||
trailerNo: '',
|
||||
vehicleType: 'self',
|
||||
carrierId: '',
|
||||
carrierName: '',
|
||||
@@ -693,6 +703,7 @@ async function handleEdit(row: Vehicle) {
|
||||
Object.assign(formData, {
|
||||
id: detail.id,
|
||||
licensePlate: detail.licensePlate,
|
||||
trailerNo: detail.trailerNo || '',
|
||||
vehicleType: detail.vehicleType,
|
||||
carrierId: detail.carrierId,
|
||||
carrierName: detail.carrierName,
|
||||
@@ -740,6 +751,7 @@ async function handleSubmit() {
|
||||
try {
|
||||
const payload = {
|
||||
licensePlate: formData.licensePlate,
|
||||
trailerNo: formData.trailerNo || undefined,
|
||||
vehicleType: formData.vehicleType,
|
||||
carrierId: formData.carrierId || undefined,
|
||||
carrierName: formData.carrierName || undefined,
|
||||
|
||||
Reference in new issue
Block a user