增加提单功能
This commit is contained in:
@@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="app-container air-bill-page">
|
||||
<basic-table
|
||||
ref="tableRef"
|
||||
:columns="columns"
|
||||
:search-columns="searchColumns"
|
||||
:query="queryList"
|
||||
row-key="billCode"
|
||||
:search-per-row="4"
|
||||
:page-sizes="[50, 100, 200, 300]"
|
||||
:page-size="100"
|
||||
:height="tableHeight"
|
||||
virtual
|
||||
>
|
||||
<template #search-billCodes="{ model }">
|
||||
<el-input
|
||||
v-model="model.billCodes"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
resize="none"
|
||||
placeholder="提单号,多个用逗号/换行分隔"
|
||||
clearable
|
||||
/>
|
||||
</template>
|
||||
<template #search-shippingDateRange="{ model }">
|
||||
<el-date-picker
|
||||
v-model="model.shippingDateRange"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="-"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
<template #search-createTimeRange="{ model }">
|
||||
<el-date-picker
|
||||
v-model="model.createTimeRange"
|
||||
type="datetimerange"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="-"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
<template #col-billCode="{ row }">
|
||||
<el-button link type="primary" @click="openDetail(row)">{{ row.billCode || '-' }}</el-button>
|
||||
</template>
|
||||
<template #col-action="{ row }">
|
||||
<el-button link type="primary" v-hasPermi="['price:airBill:query']" @click="openDetail(row)">详情</el-button>
|
||||
</template>
|
||||
</basic-table>
|
||||
|
||||
<air-bill-detail v-model="open" :title="title" :loading="loading" :detail="detail" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AirBill">
|
||||
import { listAirBill, getAirBill } from '@/api/price/airBill'
|
||||
import AirBillDetail from './components/AirBillDetail.vue'
|
||||
|
||||
const tableRef = ref()
|
||||
const tableHeight = ref(480)
|
||||
let tableResizeObserver = null
|
||||
|
||||
const open = ref(false)
|
||||
const title = ref('提单详情')
|
||||
const loading = ref(false)
|
||||
const detail = ref(null)
|
||||
|
||||
const columns = [
|
||||
{ label: '提单号', prop: 'billCode', minWidth: 150, fixed: 'left', slot: 'billCode', showOverflowTooltip: true },
|
||||
{ label: '首程落地时间', prop: 'firstLandingTime', width: 170, parseTime: true },
|
||||
{ label: '航班号', prop: 'airNo', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '实际航班号', prop: 'actualAirNo', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '航空公司代理', prop: 'airCompanyAgent', minWidth: 160, showOverflowTooltip: true },
|
||||
{ label: '目的港代理编码', prop: 'desPortAgentCode', width: 130 },
|
||||
{ label: '报关公司编码', prop: 'declareCode', width: 120 },
|
||||
{ label: '清关编码', prop: 'clearanceCode', width: 110 },
|
||||
{ label: '清关代理', prop: 'qingguanAgent', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '出仓仓库地址', prop: 'stockAddr', minWidth: 140, showOverflowTooltip: true },
|
||||
{ label: '提单类型', prop: 'billType', width: 100, align: 'center', dict: 'srv_air_bill_type' },
|
||||
{ label: '始发机场', prop: 'startAirport', width: 100 },
|
||||
{ label: '目的机场', prop: 'endAirport', width: 100 },
|
||||
{ label: '目的港', prop: 'desPort', width: 100 },
|
||||
{ label: '航线名称', prop: 'routeName', minWidth: 120, showOverflowTooltip: true },
|
||||
{ label: '航空公司', prop: 'airCompany', minWidth: 110, showOverflowTooltip: true },
|
||||
{ label: '报关费', prop: 'baoguanFee', width: 100, align: 'right' },
|
||||
{ label: '报关其他费用', prop: 'baoguanOtherFee', width: 120, align: 'right' },
|
||||
{ label: '仓储费', prop: 'stockFee', width: 100, align: 'right' },
|
||||
{ label: '目的港代理费', prop: 'desPortAgentFee', width: 120, align: 'right' },
|
||||
{ label: '空运其他费用', prop: 'airOtherFee', width: 120, align: 'right' },
|
||||
{ label: '其他费用', prop: 'otherFee', width: 100, align: 'right' },
|
||||
{ label: '总件数', prop: 'pieceNumber', width: 90, align: 'center' },
|
||||
{ label: '总体积', prop: 'volume', width: 100, align: 'right' },
|
||||
{ label: '实际重量', prop: 'actualWeight', width: 100, align: 'right' },
|
||||
{ label: '实际体积', prop: 'actualVolume', width: 100, align: 'right' },
|
||||
{ label: '体积重', prop: 'volumeWeight', width: 100, align: 'right' },
|
||||
{ label: '发货日期', prop: 'shippingDate', width: 170, parseTime: true },
|
||||
{ label: '起飞时间', prop: 'startTime', width: 170, parseTime: true },
|
||||
{ label: '到达时间', prop: 'arriveTime', width: 170, parseTime: true },
|
||||
{ label: '到仓时间', prop: 'arriveStockTime', width: 170, parseTime: true },
|
||||
{ label: '清关时间', prop: 'clearanceTime', width: 170, parseTime: true },
|
||||
{ label: '操作', prop: 'action', align: 'center', width: 90, fixed: 'right', slot: 'action', showInToolbar: false, showOverflowTooltip: false }
|
||||
]
|
||||
|
||||
const searchColumns = [
|
||||
{ label: '提单号', prop: 'billCodes', span: 6, slot: 'billCodes' },
|
||||
{ label: '始发机场', prop: 'startAirport', span: 6 },
|
||||
{ label: '目的机场', prop: 'endAirport', span: 6 },
|
||||
{ label: '目的港', prop: 'desPort', span: 6 },
|
||||
{ label: '航线名称', prop: 'routeName', span: 6 },
|
||||
{ label: '航空公司', prop: 'airCompany', span: 6 },
|
||||
{ label: '航班号', prop: 'airNo', span: 6 },
|
||||
{ label: '提单类型', prop: 'billType', span: 6, component: 'select', dict: 'srv_air_bill_type', placeholder: '请选择' },
|
||||
{ label: '发货日期', prop: 'shippingDateRange', span: 12, slot: 'shippingDateRange' },
|
||||
{ label: '创建时间', prop: 'createTimeRange', span: 12, slot: 'createTimeRange' }
|
||||
]
|
||||
|
||||
function parseBillCodes(raw) {
|
||||
const values = Array.isArray(raw) ? raw : [raw]
|
||||
return [...new Set(
|
||||
values
|
||||
.filter(v => v !== null && v !== undefined)
|
||||
.flatMap(v => String(v).split(/[\s,,;;]+/))
|
||||
.map(v => v.trim())
|
||||
.filter(Boolean)
|
||||
)]
|
||||
}
|
||||
|
||||
function normalizeQuery(params = {}) {
|
||||
const query = { ...params }
|
||||
const codes = parseBillCodes(query.billCodes)
|
||||
if (codes.length === 1) {
|
||||
query.billCode = codes[0]
|
||||
delete query.billCodes
|
||||
} else if (codes.length > 1) {
|
||||
query.billCodes = codes
|
||||
} else {
|
||||
delete query.billCodes
|
||||
delete query.billCode
|
||||
}
|
||||
const ship = query.shippingDateRange
|
||||
if (Array.isArray(ship) && ship.length === 2) {
|
||||
query.shippingDateBegin = ship[0]
|
||||
query.shippingDateEnd = ship[1]
|
||||
}
|
||||
delete query.shippingDateRange
|
||||
const create = query.createTimeRange
|
||||
if (Array.isArray(create) && create.length === 2) {
|
||||
query.createTimeBegin = create[0]
|
||||
query.createTimeEnd = create[1]
|
||||
}
|
||||
delete query.createTimeRange
|
||||
if (query.billType === '' || query.billType === null || query.billType === undefined) {
|
||||
delete query.billType
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
function queryList(params) {
|
||||
return listAirBill(normalizeQuery(params))
|
||||
}
|
||||
|
||||
function openDetail(row) {
|
||||
if (!row?.billCode) return
|
||||
open.value = true
|
||||
title.value = '提单详情 - ' + row.billCode
|
||||
loading.value = true
|
||||
detail.value = null
|
||||
getAirBill(row.billCode).then(res => {
|
||||
detail.value = res.data || null
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function updateTableHeight() {
|
||||
const tableEl = document.querySelector('.air-bill-page > .basic-table .el-table, .air-bill-page > .basic-table .basic-table-virtual')
|
||||
if (!tableEl) return
|
||||
const top = tableEl.getBoundingClientRect().top
|
||||
tableHeight.value = Math.max(240, Math.floor(window.innerHeight - top - 70))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateTableHeight()
|
||||
const form = document.querySelector('.air-bill-page > .basic-table .basic-form')
|
||||
if (form && typeof ResizeObserver !== 'undefined') {
|
||||
tableResizeObserver = new ResizeObserver(() => updateTableHeight())
|
||||
tableResizeObserver.observe(form)
|
||||
}
|
||||
})
|
||||
window.addEventListener('resize', updateTableHeight)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', updateTableHeight)
|
||||
if (tableResizeObserver) {
|
||||
tableResizeObserver.disconnect()
|
||||
tableResizeObserver = null
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:model-value="modelValue"
|
||||
:title="title"
|
||||
size="840px"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
class="air-bill-detail-drawer"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<div v-loading="loading" class="air-bill-detail-wrap">
|
||||
<el-empty v-if="!detail" description="暂无详情" />
|
||||
<div v-else class="air-bill-detail">
|
||||
<section class="detail-block">
|
||||
<div class="sec-title">基本信息</div>
|
||||
<el-descriptions class="detail-desc" :column="2" border size="small">
|
||||
<el-descriptions-item label="提单号">{{ detail.billCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="提单类型"><dict-tag :options="srv_air_bill_type" :value="detail.billType" /></el-descriptions-item>
|
||||
<el-descriptions-item label="始发机场">{{ detail.startAirport || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目的机场">{{ detail.endAirport || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目的港">{{ detail.desPort || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="航线名称">{{ detail.routeName || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="航空公司">{{ detail.airCompany || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总件数">{{ numOrDash(detail.pieceNumber) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="总体积">{{ numOrDash(detail.volume) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实际重量">{{ numOrDash(detail.actualWeight) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实际体积">{{ numOrDash(detail.actualVolume) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="体积重">{{ numOrDash(detail.volumeWeight) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section>
|
||||
|
||||
<section class="detail-block">
|
||||
<div class="sec-title">费用信息</div>
|
||||
<el-descriptions class="detail-desc" :column="2" border size="small">
|
||||
<el-descriptions-item label="报关费">{{ money(detail.baoguanFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报关其他费用">{{ money(detail.baoguanOtherFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="仓储费">{{ money(detail.stockFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目的港代理费">{{ money(detail.desPortAgentFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="空运其他费用">{{ money(detail.airOtherFee) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="其他费用">{{ money(detail.otherFee) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section>
|
||||
|
||||
<section class="detail-block">
|
||||
<div class="sec-title">航班与代理</div>
|
||||
<el-descriptions class="detail-desc" :column="2" border size="small">
|
||||
<el-descriptions-item label="航班号">{{ detail.airNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="实际航班号">{{ detail.actualAirNo || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="航空公司代理">{{ detail.airCompanyAgent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="目的港代理编码">{{ detail.desPortAgentCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报关公司编码">{{ detail.declareCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="清关编码">{{ detail.clearanceCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="清关代理">{{ detail.qingguanAgent || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出仓仓库地址">{{ detail.stockAddr || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section>
|
||||
|
||||
<section class="detail-block">
|
||||
<div class="sec-title">时效信息</div>
|
||||
<el-descriptions class="detail-desc" :column="2" border size="small">
|
||||
<el-descriptions-item label="首程落地时间">{{ formatDateTime(detail.firstLandingTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发货日期">{{ formatDateTime(detail.shippingDate) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="起飞时间">{{ formatDateTime(detail.startTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="到达时间">{{ formatDateTime(detail.arriveTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="到仓时间">{{ formatDateTime(detail.arriveStockTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="清关时间">{{ formatDateTime(detail.clearanceTime) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup name="AirBillDetail">
|
||||
import { parseTime } from '@/utils/bms'
|
||||
|
||||
const { srv_air_bill_type } = useDict('srv_air_bill_type')
|
||||
|
||||
defineProps({
|
||||
modelValue: { type: Boolean, default: false },
|
||||
title: { type: String, default: '提单详情' },
|
||||
loading: { type: Boolean, default: false },
|
||||
detail: { type: Object, default: null }
|
||||
})
|
||||
|
||||
defineEmits(['update:modelValue'])
|
||||
|
||||
function numOrDash(v) {
|
||||
return v === null || v === undefined || v === '' ? '-' : v
|
||||
}
|
||||
|
||||
function money(v) {
|
||||
if (v === null || v === undefined || v === '') return '-'
|
||||
const n = Number(v)
|
||||
if (Number.isNaN(n)) return String(v)
|
||||
return n.toFixed(4)
|
||||
}
|
||||
|
||||
function formatDateTime(v) {
|
||||
return v ? parseTime(v) : '-'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.air-bill-detail-wrap {
|
||||
min-height: 200px;
|
||||
}
|
||||
.detail-block + .detail-block {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.sec-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: var(--el-text-color-primary);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user