调整业务模块

This commit is contained in:
2026-09-07 10:05:32 +08:00
parent 6a6d10dc79
commit 0d070d4b34
16 changed files with 264 additions and 157 deletions
+2
View File
@@ -4,6 +4,8 @@ const baseUrl = '/blade-transport/formal-settlement';
export const getList = (current, size, params) => export const getList = (current, size, params) =>
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } }); request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
export const exportList = params =>
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } }); export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
export const getCandidates = (current, size, params) => export const getCandidates = (current, size, params) =>
request({ request({
@@ -4,6 +4,8 @@ const baseUrl = '/blade-transport/transport-reconciliation';
export const getList = (current, size, params) => export const getList = (current, size, params) =>
request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } }); request({ url: `${baseUrl}/list`, method: 'get', params: { current, size, ...params } });
export const exportList = params =>
request({ url: `${baseUrl}/export`, method: 'get', params, responseType: 'blob' });
export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } }); export const getDetail = id => request({ url: `${baseUrl}/detail`, method: 'get', params: { id } });
export const getFormalOptions = (current, size, params) => export const getFormalOptions = (current, size, params) =>
request({ request({
@@ -335,7 +335,12 @@ export default {
const data = (file.response && file.response.data) || file.data || file; const data = (file.response && file.response.data) || file.data || file;
const url = data.link || data.url || data.domain || file.url || ''; const url = data.link || data.url || data.domain || file.url || '';
const originalName = const originalName =
data.originalName || file.originalName || data.name || file.name || this.getFileName(url) || '附件'; data.originalName ||
file.originalName ||
file.name ||
data.name ||
this.getFileName(url) ||
'附件';
const extension = this.getExtension({ name: originalName, url }); const extension = this.getExtension({ name: originalName, url });
return { return {
...data, ...data,
+12
View File
@@ -152,6 +152,18 @@ export default [
}, },
], ],
}, },
{
path: '/business/waybill-import',
component: Layout,
children: [
{
path: '',
name: '导入运单',
meta: { keepAlive: false, activeMenu: '/business/waybill-import' },
component: () => import('@/views/business/waybill-import.vue'),
},
],
},
{ {
path: '/business/loading-manage/form', path: '/business/loading-manage/form',
component: Layout, component: Layout,
@@ -3268,12 +3268,6 @@
</template> </template>
</el-dialog> </el-dialog>
<waybill-import-dialog
v-if="config.enableWaybillImport"
v-model="waybillImportBox"
@closed="refreshChange"
/>
<flow-design <flow-design
v-if="website.design.designMode" v-if="website.design.designMode"
is-dialog is-dialog
@@ -5271,7 +5265,6 @@ import { h, resolveComponent } from 'vue';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import 'nprogress/nprogress.css'; import 'nprogress/nprogress.css';
import WaybillImportDialog from './waybill-import-dialog.vue';
import BillingPlanEditor from './billing-plan-editor.vue'; import BillingPlanEditor from './billing-plan-editor.vue';
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315'; const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
@@ -5511,7 +5504,6 @@ const taskCarrierTypes = ['承运商', '自运', '网货平台'];
export default { export default {
components: { components: {
WaybillImportDialog,
BillingPlanEditor, BillingPlanEditor,
PageAvueForm, PageAvueForm,
Rank, Rank,
@@ -5611,7 +5603,6 @@ export default {
currentPage: 1, currentPage: 1,
total: 0, total: 0,
}, },
waybillImportBox: false,
transportPlanImportBox: false, transportPlanImportBox: false,
transportPlanImportLoading: false, transportPlanImportLoading: false,
transportPlanImportFiles: [], transportPlanImportFiles: [],
@@ -11554,7 +11545,7 @@ export default {
}, },
handleImport() { handleImport() {
if (this.config.enableWaybillImport) { if (this.config.enableWaybillImport) {
this.waybillImportBox = true; this.$router.push({ path: '/business/waybill-import' });
return; return;
} }
if (this.config.enableTransportPlanImport) { if (this.config.enableTransportPlanImport) {
@@ -114,7 +114,7 @@
<el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form"> <el-form :model="route" label-position="right" label-width="auto" class="dispatch-form carrier-form">
<el-row :gutter="16"> <el-row :gutter="16">
<template v-if="isRoad(route)"> <template v-if="isRoad(route)">
<el-col :span="6"><el-form-item label="承运商" :required="route.carrierType === '承运商'"><el-select :model-value="route.carrierType === '承运商' ? route.carrierContractId : route.carrierName" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="carrierOptionValue(item)" :label="carrierOptionLabel(item)" :value="route.carrierType === '承运商' ? carrierOptionValue(item) : item.carrierName" /></el-select></el-form-item></el-col> <el-col :span="6"><el-form-item label="承运商" :required="route.carrierType !== '自运'"><el-select :model-value="route.carrierType !== '自运' ? route.carrierContractId : route.carrierName" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="carrierOptionValue(item)" :label="carrierOptionLabel(item)" :value="route.carrierType !== '自运' ? carrierOptionValue(item) : item.carrierName" /></el-select></el-form-item></el-col>
<el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="司机" :required="route.carrierType !== '承运商'"><el-autocomplete :ref="element => setDriverInput(route.segmentNo, element)" v-model="route.driverName" :debounce="300" :fetch-suggestions="fetchDriverSuggestions" clearable placeholder="请输入司机" :loading="driverLoading" @select="item => handleDriverSuggestionSelect(route, item)" /></el-form-item></el-col>
<el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item :label="route.carrierType === '承运商' ? '手机号' : '司机手机号'" :required="route.carrierType !== '承运商'"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="车牌号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
@@ -123,7 +123,7 @@
<el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人手机号"><el-input v-model="route.escortPhone" placeholder="请输入" /></el-form-item></el-col> <el-col v-if="route.carrierType !== '承运商'" :span="6"><el-form-item label="押运人手机号"><el-input v-model="route.escortPhone" placeholder="请输入" /></el-form-item></el-col>
</template> </template>
<template v-else> <template v-else>
<el-col :span="6"><el-form-item label="承运商" :required="route.carrierType === '承运商'"><el-select :model-value="route.carrierType === '承运商' ? route.carrierContractId : route.carrierName" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="carrierOptionValue(item)" :label="carrierOptionLabel(item)" :value="route.carrierType === '承运商' ? carrierOptionValue(item) : item.carrierName" /></el-select></el-form-item></el-col> <el-col :span="6"><el-form-item label="承运商" :required="route.carrierType !== '自运'"><el-select :model-value="route.carrierType !== '自运' ? route.carrierContractId : route.carrierName" filterable clearable placeholder="请选择" :loading="carrierLoading" @change="value => handleCarrierContractChange(route, value)"><el-option v-for="item in carrierOptions" :key="carrierOptionValue(item)" :label="carrierOptionLabel(item)" :value="route.carrierType !== '自运' ? carrierOptionValue(item) : item.carrierName" /></el-select></el-form-item></el-col>
<el-col :span="6"><el-form-item label="船/航/班列号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="船/航/班列号" required><el-input v-model="route.vehicleNo" placeholder="请输入" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="船长"><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="船长"><el-input v-model="route.captainName" placeholder="请输入" /></el-form-item></el-col>
<el-col :span="6"><el-form-item label="联系电话"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col> <el-col :span="6"><el-form-item label="联系电话"><el-input v-model="route.driverPhone" placeholder="请输入" /></el-form-item></el-col>
@@ -689,16 +689,17 @@ export default {
}, },
handleCarrierTypeChange(route, value) { handleCarrierTypeChange(route, value) {
route.carrierType = value; route.carrierType = value;
route.carrierName = '';
route.carrierId = '';
route.carrierContractId = '';
if (value === '承运商') { if (value === '承运商') {
route.trailerVehicleNo = ''; route.trailerVehicleNo = '';
route.escortName = ''; route.escortName = '';
route.escortPhone = ''; route.escortPhone = '';
} }
if (value !== '承运商') { this.loadCarrierOptions().then(() => {
route.carrierContractId = '';
if (value === '网货平台') route.carrierName = '';
if (value === '自运') this.applySelfOperatedCarrierDefaults(route); if (value === '自运') this.applySelfOperatedCarrierDefaults(route);
} });
}, },
handleCarrierContractChange(route, contractId) { handleCarrierContractChange(route, contractId) {
const contract = this.carrierOptions.find( const contract = this.carrierOptions.find(
@@ -707,7 +708,8 @@ export default {
value => String(value || '') === String(contractId || '') value => String(value || '') === String(contractId || '')
) )
); );
route.carrierContractId = route.carrierType === '承运商' ? contract?.contractId || '' : ''; route.carrierContractId = route.carrierType !== '自运' ? contract?.contractId || '' : '';
route.carrierId = route.carrierType !== '自运' ? contract?.carrierId || '' : '';
route.carrierName = contract?.carrierName || ''; route.carrierName = contract?.carrierName || '';
}, },
carrierOptionValue(item = {}) { carrierOptionValue(item = {}) {
@@ -733,7 +735,26 @@ export default {
async loadCarrierOptions() { async loadCarrierOptions() {
this.carrierLoading = true; this.carrierLoading = true;
try { try {
this.carrierOptions = unwrapRecords(await api.getCarriers(this.id)); this.carrierOptions = unwrapRecords(await api.getCarriers(this.id))
.map(item => ({
...item,
contractId: item.contractId || item.carrierContractId || item.id || '',
carrierName:
item.partyB ||
item.partyBName ||
item.contractPartyB ||
item.customerContractPartyB ||
item.carrierName ||
'',
carrierId:
item.partyBId ||
item.partyBUserId ||
item.contractPartyBId ||
item.customerContractPartyBId ||
item.carrierId ||
'',
}))
.filter(item => item.carrierName);
} finally { } finally {
this.carrierLoading = false; this.carrierLoading = false;
} }
@@ -7296,16 +7296,13 @@ export default {
}, },
handleDispatchCarrierTypeChange(value) { handleDispatchCarrierTypeChange(value) {
const nextValue = value || ''; const nextValue = value || '';
this.dispatchItemForm.carrierName = '';
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierContractId = '';
if (nextValue === '承运商') { if (nextValue === '承运商') {
this.dispatchItemForm.trailerVehicleNo = ''; this.dispatchItemForm.trailerVehicleNo = '';
this.dispatchItemForm.escortName = ''; this.dispatchItemForm.escortName = '';
this.dispatchItemForm.escortPhone = ''; this.dispatchItemForm.escortPhone = '';
} else {
this.dispatchItemForm.carrierContractId = '';
if (nextValue === '网货平台') {
this.dispatchItemForm.carrierId = '';
this.dispatchItemForm.carrierName = '';
}
} }
this.dispatchItemForm.carrierType = nextValue; this.dispatchItemForm.carrierType = nextValue;
this.loadDispatchCarrierOptions(this.dispatchRow); this.loadDispatchCarrierOptions(this.dispatchRow);
@@ -7379,8 +7376,11 @@ export default {
}); });
return Promise.all([detailPromise, contractPromise]) return Promise.all([detailPromise, contractPromise])
.then(([, carrierContracts]) => { .then(([, carrierContracts]) => {
if (carrierTypeAtRequest === '承运商') { if (carrierTypeAtRequest !== '自运') {
this.dispatchCarrierOptions = carrierContracts || []; this.dispatchCarrierOptions = (carrierContracts || []).map(item => ({
...item,
value: item.carrierContractId || item.id,
}));
const current = this.dispatchCarrierOptions.find( const current = this.dispatchCarrierOptions.find(
item => item =>
String(item.carrierContractId) === String(this.dispatchItemForm.carrierContractId) String(item.carrierContractId) === String(this.dispatchItemForm.carrierContractId)
@@ -7408,19 +7408,37 @@ export default {
}, },
getWaybillCarrierContractOptions(contracts = []) { getWaybillCarrierContractOptions(contracts = []) {
const counts = contracts.reduce((result, item) => { const counts = contracts.reduce((result, item) => {
const name = String(item.partyB || '').trim(); const name = String(
item.partyB ||
item.partyBName ||
item.contractPartyB ||
item.customerContractPartyB ||
''
).trim();
if (name) result[name] = (result[name] || 0) + 1; if (name) result[name] = (result[name] || 0) + 1;
return result; return result;
}, {}); }, {});
return contracts return contracts
.map(contract => { .map(contract => {
const carrierName = String(contract.partyB || '').trim(); const carrierName = String(
contract.partyB ||
contract.partyBName ||
contract.contractPartyB ||
contract.customerContractPartyB ||
''
).trim();
if (!carrierName || !contract.id) return null; if (!carrierName || !contract.id) return null;
const identifier = contract.contractName || contract.contractNo || contract.id; const identifier = contract.contractName || contract.contractNo || contract.id;
return { return {
id: contract.id, id: contract.id,
value: contract.id, value: contract.id,
carrierContractId: contract.id, carrierContractId: contract.id,
carrierId:
contract.partyBId ||
contract.partyBUserId ||
contract.contractPartyBId ||
contract.customerContractPartyBId ||
'',
carrierName, carrierName,
fullName: carrierName, fullName: carrierName,
label: counts[carrierName] > 1 ? `${carrierName}${identifier}` : carrierName, label: counts[carrierName] > 1 ? `${carrierName}${identifier}` : carrierName,
@@ -7464,9 +7482,9 @@ export default {
const optionValue = this.dispatchIsCarrierMode ? item.carrierContractId : item.value; const optionValue = this.dispatchIsCarrierMode ? item.carrierContractId : item.value;
return String(optionValue || '') === String(value || ''); return String(optionValue || '') === String(value || '');
}); });
if (this.dispatchIsCarrierMode) { if (this.dispatchItemForm.carrierType !== '自运') {
this.dispatchItemForm.carrierContractId = carrier?.carrierContractId || ''; this.dispatchItemForm.carrierContractId = carrier?.carrierContractId || '';
this.dispatchItemForm.carrierId = ''; this.dispatchItemForm.carrierId = carrier?.carrierId || '';
this.dispatchItemForm.carrierName = carrier?.carrierName || ''; this.dispatchItemForm.carrierName = carrier?.carrierName || '';
return; return;
} }
@@ -1,10 +1,12 @@
<template> <template>
<el-dialog <component
:is="standalone ? 'div' : 'el-dialog'"
v-model="visible" v-model="visible"
title="导入运单" title="导入运单"
width="90%" width="90%"
append-to-body append-to-body
destroy-on-close destroy-on-close
:class="{ 'waybill-import-page': standalone }"
@closed="handleClosed" @closed="handleClosed"
> >
<div class="waybill-import-toolbar"> <div class="waybill-import-toolbar">
@@ -96,7 +98,7 @@
@current-change="loadBatches" @current-change="loadBatches"
@size-change="loadBatches" @size-change="loadBatches"
/> />
</el-dialog> </component>
<el-dialog v-model="detailVisible" title="导入运单详情" width="90%" append-to-body> <el-dialog v-model="detailVisible" title="导入运单详情" width="90%" append-to-body>
<el-form :inline="true" :model="detailQuery" <el-form :inline="true" :model="detailQuery"
@@ -423,7 +425,7 @@
</template> </template>
<script setup> <script setup>
import { computed, reactive, ref } from 'vue'; import { computed, onMounted, reactive, ref, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import { getList as getCargoTypeList } from '@/api/base/cargo-type'; import { getList as getCargoTypeList } from '@/api/base/cargo-type';
import { getList as getCommonCargoList } from '@/api/business/common-cargo'; import { getList as getCommonCargoList } from '@/api/business/common-cargo';
@@ -434,7 +436,7 @@ import { getDictionary } from '@/api/system/dictbiz';
import * as api from '@/api/business/waybill-manage'; import * as api from '@/api/business/waybill-manage';
import { downloadXls } from '@/utils/util'; import { downloadXls } from '@/utils/util';
const props = defineProps({ modelValue: Boolean }); const props = defineProps({ modelValue: Boolean, standalone: Boolean });
const emit = defineEmits(['update:modelValue', 'closed']); const emit = defineEmits(['update:modelValue', 'closed']);
const visible = computed({ const visible = computed({
get: () => props.modelValue, get: () => props.modelValue,
@@ -753,6 +755,15 @@ const loadBatches = async () => {
batches.value = res.data?.data?.records || []; batches.value = res.data?.data?.records || [];
page.total = res.data?.data?.total || 0; page.total = res.data?.data?.total || 0;
}; };
onMounted(() => {
if (props.standalone) loadBatches();
});
watch(
() => props.modelValue,
value => {
if (value) loadBatches();
}
);
const resetQuery = () => { const resetQuery = () => {
Object.assign(query, { batchNo: '', carrierId: '', createUser: '', createTimeRange: [] }); Object.assign(query, { batchNo: '', carrierId: '', createUser: '', createTimeRange: [] });
page.current = 1; page.current = 1;
@@ -1083,6 +1094,12 @@ const confirmImport = async () => {
} }
} }
.waybill-import-page {
min-height: calc(100vh - 120px);
padding: 12px 24px 24px;
background: #fff;
}
.waybill-import-create { .waybill-import-create {
&__form-panel, &__form-panel,
&__detail-panel { &__detail-panel {
@@ -634,13 +634,7 @@
item.fullName || item.fullName ||
item.name item.name
" "
:value=" :value="taskCarrierOptionValue(item)"
item.value ||
item.customerName ||
item.carrierName ||
item.fullName ||
item.name
"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -1188,13 +1182,7 @@
item.fullName || item.fullName ||
item.name item.name
" "
:value=" :value="taskCarrierOptionValue(item)"
item.value ||
item.customerName ||
item.carrierName ||
item.fullName ||
item.name
"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
@@ -2212,12 +2200,6 @@
</template> </template>
</el-dialog> </el-dialog>
<waybill-import-dialog
v-if="config.enableWaybillImport"
v-model="waybillImportBox"
@closed="refreshChange"
/>
<flow-design <flow-design
v-if="website.design.designMode" v-if="website.design.designMode"
is-dialog is-dialog
@@ -2827,7 +2809,6 @@ import { h, resolveComponent } from 'vue';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import NProgress from 'nprogress'; import NProgress from 'nprogress';
import 'nprogress/nprogress.css'; import 'nprogress/nprogress.css';
import WaybillImportDialog from './waybill-import-dialog.vue';
const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315'; const AMAP_KEY = '653b7cf105ad7fb8ec9b2f5198ade315';
const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a'; const AMAP_SECURITY_CODE = '5aab65c632e48ebae0d0e28f5b28e21a';
@@ -2973,7 +2954,6 @@ const getPlainAvueForm = avueForm => {
export default { export default {
components: { components: {
WaybillImportDialog,
PageAvueForm, PageAvueForm,
InfoFilled, InfoFilled,
Rank, Rank,
@@ -3073,7 +3053,6 @@ export default {
currentPage: 1, currentPage: 1,
total: 0, total: 0,
}, },
waybillImportBox: false,
draftSaveLoading: false, draftSaveLoading: false,
crudDialogType: '', crudDialogType: '',
defaultDialogButtons: null, defaultDialogButtons: null,
@@ -3363,10 +3342,9 @@ export default {
}, },
taskCarrierValue: { taskCarrierValue: {
get() { get() {
if (this.form.carrierType === '承运商') { return this.form.carrierType === '自运'
return this.form.carrierContractId || ''; ? this.form.carrierName || ''
} : this.form.carrierContractId || this.form.carrierName || '';
return this.form.carrierName || '';
}, },
set(value) { set(value) {
this.handleTaskCarrierChange(value); this.handleTaskCarrierChange(value);
@@ -5387,34 +5365,34 @@ export default {
}, },
handleTaskCarrierTypeChange(value) { handleTaskCarrierTypeChange(value) {
const nextValue = value || '承运商'; const nextValue = value || '承运商';
this.form.carrierName = '';
this.form.carrierId = '';
this.form.carrierContractId = '';
if (nextValue === '承运商') { if (nextValue === '承运商') {
this.form.trailerVehicleNo = ''; this.form.trailerVehicleNo = '';
this.form.escortName = ''; this.form.escortName = '';
this.form.escortPhone = ''; this.form.escortPhone = '';
} else if (!this.isTaskCarrierRequired(nextValue)) {
this.form.carrierName = '';
this.form.carrierId = '';
}
if (nextValue !== '承运商') {
this.form.carrierContractId = '';
} }
this.form.carrierType = nextValue; this.form.carrierType = nextValue;
if (nextValue === '自运') {
const contract = this.contractOptions.find(
item => String(item.id) === String(this.form.contractId)
);
this.fillSelfOperatedCarrierFromContract(contract);
}
this.taskCarrierRequestId += 1; this.taskCarrierRequestId += 1;
this.taskCarrierOptions = []; this.taskCarrierOptions = [];
this.taskCarrierLoading = false; this.taskCarrierLoading = false;
if (nextValue === '自运' && this.form.projectId) { this.resetWaybillCarrierContractState();
//
// 使
if (this.form.projectId) {
this.loadTaskCarrierOptions(); this.loadTaskCarrierOptions();
} }
}, },
fillSelfOperatedCarrierFromContract(contract = {}) { fillSelfOperatedCarrierFromContract(contract = {}) {
if (this.form.carrierType !== '自运') return; if (this.form.carrierType !== '自运') return;
const carrierName = String(contract.partyB || contract.partyBName || '').trim(); const carrierName = String(
contract.partyB ||
contract.partyBName ||
contract.contractPartyB ||
contract.customerContractPartyB ||
''
).trim();
if (!carrierName) return; if (!carrierName) return;
const exists = this.taskCarrierOptions.some( const exists = this.taskCarrierOptions.some(
item => String(item.value || item.carrierName || '') === carrierName item => String(item.value || item.carrierName || '') === carrierName
@@ -5433,6 +5411,19 @@ export default {
isTaskCarrierRequired(carrierType) { isTaskCarrierRequired(carrierType) {
return ['承运商', '网货平台'].includes(String(carrierType || '').trim()); return ['承运商', '网货平台'].includes(String(carrierType || '').trim());
}, },
taskCarrierOptionValue(item = {}) {
if (this.form.carrierType !== '自运') {
return item.carrierContractId || item.value || item.id || '';
}
return (
item.carrierName ||
item.customerName ||
item.fullName ||
item.name ||
item.value ||
''
);
},
getProjectCarrierOptions(project = {}) { getProjectCarrierOptions(project = {}) {
const rows = this.parseJsonArray(project.carrierJson); const rows = this.parseJsonArray(project.carrierJson);
const names = String(project.carrierNames || '') const names = String(project.carrierNames || '')
@@ -5462,18 +5453,42 @@ export default {
}, },
getWaybillCarrierContractOptions(contracts = []) { getWaybillCarrierContractOptions(contracts = []) {
const carrierNameCounts = contracts.reduce((counts, contract) => { const carrierNameCounts = contracts.reduce((counts, contract) => {
const carrierName = String(contract.partyA || contract.partyAName || '').trim(); const carrierName = String(
contract.partyB ||
contract.partyBName ||
contract.contractPartyB ||
contract.customerContractPartyB ||
contract.secondParty ||
contract.secondPartyName ||
''
).trim();
if (carrierName) counts[carrierName] = (counts[carrierName] || 0) + 1; if (carrierName) counts[carrierName] = (counts[carrierName] || 0) + 1;
return counts; return counts;
}, {}); }, {});
return contracts return contracts
.map(contract => { .map(contract => {
const carrierName = String(contract.partyA || contract.partyAName || '').trim(); const carrierName = String(
contract.partyB ||
contract.partyBName ||
contract.contractPartyB ||
contract.customerContractPartyB ||
contract.secondParty ||
contract.secondPartyName ||
''
).trim();
if (!carrierName || !contract.id) return null; if (!carrierName || !contract.id) return null;
const contractIdentifier = contract.contractName || contract.contractNo || contract.id; const contractIdentifier = contract.contractName || contract.contractNo || contract.id;
return { return {
id: contract.id, id: contract.id,
carrierId: contract.partyAId || contract.partyAUserId || contract.customerId || '', carrierId:
contract.partyBId ||
contract.partyBUserId ||
contract.contractPartyBId ||
contract.customerContractPartyBId ||
contract.secondPartyId ||
contract.secondPartyUserId ||
contract.carrierId ||
'',
value: contract.id, value: contract.id,
label: label:
carrierNameCounts[carrierName] > 1 carrierNameCounts[carrierName] > 1
@@ -5534,7 +5549,7 @@ export default {
...project, ...project,
id: projectId, id: projectId,
}); });
if (carrierTypeAtRequest === '承运商') { if (carrierTypeAtRequest !== '自运') {
if (!projectId) { if (!projectId) {
this.taskCarrierOptions = []; this.taskCarrierOptions = [];
return Promise.resolve([]); return Promise.resolve([]);
@@ -5545,12 +5560,6 @@ export default {
if (requestId !== this.taskCarrierRequestId) return this.taskCarrierOptions; if (requestId !== this.taskCarrierRequestId) return this.taskCarrierOptions;
if (options === null) return this.taskCarrierOptions; if (options === null) return this.taskCarrierOptions;
this.taskCarrierOptions = options || []; this.taskCarrierOptions = options || [];
if (!this.taskCarrierOptions.length && this.form.carrierType === '自运') {
const customerContract = this.contractOptions.find(
item => String(item.id) === String(this.form.contractId)
);
this.fillSelfOperatedCarrierFromContract(customerContract);
}
return this.taskCarrierOptions; return this.taskCarrierOptions;
}) })
.catch(() => this.taskCarrierOptions) .catch(() => this.taskCarrierOptions)
@@ -5599,14 +5608,19 @@ export default {
}, },
handleTaskCarrierChange(value) { handleTaskCarrierChange(value) {
const carrier = this.taskCarrierOptions.find(item => const carrier = this.taskCarrierOptions.find(item =>
[item.value, item.customerName, item.carrierName, item.fullName, item.name].some( [
name => String(name || '') === String(value || '') this.taskCarrierOptionValue(item),
) item.value,
item.customerName,
item.carrierName,
item.fullName,
item.name,
].some(name => String(name || '') === String(value || ''))
); );
if (this.form.carrierType === '承运商') { if (this.form.carrierType !== '自运') {
this.form.carrierContractId = carrier?.carrierContractId || ''; this.form.carrierContractId = carrier?.carrierContractId || '';
this.form.carrierId = carrier?.carrierId || ''; this.form.carrierId = carrier?.carrierId || '';
this.form.carrierName = carrier?.carrierName || value || ''; this.form.carrierName = carrier?.carrierName || '';
return; return;
} }
this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || ''; this.form.carrierId = carrier?.carrierContractId ? '' : carrier?.id || '';
@@ -7537,7 +7551,7 @@ export default {
}, },
handleImport() { handleImport() {
if (this.config.enableWaybillImport) { if (this.config.enableWaybillImport) {
this.waybillImportBox = true; this.$router.push({ path: '/business/waybill-import' });
return; return;
} }
const column = this.findColumn(this.excelOptionConfig.column, 'excelFile'); const column = this.findColumn(this.excelOptionConfig.column, 'excelFile');
+26 -5
View File
@@ -2464,6 +2464,9 @@ export default {
}); });
}, },
handleCarrierTypeChange() { handleCarrierTypeChange() {
this.dialogForm.carrierContractId = '';
this.dialogForm.carrierName = '';
this.dialogForm.carrierId = '';
if (this.dialogForm.carrierType === '承运商') { if (this.dialogForm.carrierType === '承运商') {
this.dialogForm.trailerVehicleNo = ''; this.dialogForm.trailerVehicleNo = '';
this.dialogForm.escortName = ''; this.dialogForm.escortName = '';
@@ -2471,7 +2474,6 @@ export default {
} }
if (this.dialogForm.carrierType === '网货平台') { if (this.dialogForm.carrierType === '网货平台') {
this.dialogForm.carrierContractId = ''; this.dialogForm.carrierContractId = '';
this.dialogForm.carrierName = '';
} }
this.loadTaskCarrierOptions(); this.loadTaskCarrierOptions();
}, },
@@ -2626,19 +2628,38 @@ export default {
const response = await loadingApi.getCarrierContracts(projectIds); const response = await loadingApi.getCarrierContracts(projectIds);
if (requestId !== this.taskCarrierRequestId) return []; if (requestId !== this.taskCarrierRequestId) return [];
const contracts = response.data?.data || response.data || []; const contracts = response.data?.data || response.data || [];
if (this.dialogForm.carrierType === '承运商') { if (this.dialogForm.carrierType !== '自运') {
this.taskCarrierOptions = contracts; this.taskCarrierOptions = contracts
.map(item => ({
...item,
carrierName:
item.partyB ||
item.partyBName ||
item.contractPartyB ||
item.customerContractPartyB ||
item.carrierName ||
'',
carrierId:
item.partyBId ||
item.partyBUserId ||
item.contractPartyBId ||
item.customerContractPartyBId ||
item.carrierId ||
'',
}))
.filter(item => item.carrierName);
const current = const current =
contracts.find(item => String(item.id) === String(this.dialogForm.carrierContractId)) || contracts.find(item => String(item.id) === String(this.dialogForm.carrierContractId)) ||
contracts.find(item => item.carrierName === this.dialogForm.carrierName); this.taskCarrierOptions.find(item => item.carrierName === this.dialogForm.carrierName);
if (current) { if (current) {
this.dialogForm.carrierContractId = current.id; this.dialogForm.carrierContractId = current.id;
this.dialogForm.carrierName = current.carrierName; this.dialogForm.carrierName = current.carrierName;
this.dialogForm.carrierId = current.carrierId || '';
} else { } else {
this.dialogForm.carrierContractId = ''; this.dialogForm.carrierContractId = '';
this.dialogForm.carrierName = ''; this.dialogForm.carrierName = '';
} }
return contracts; return this.taskCarrierOptions;
} }
if (this.dialogForm.carrierType === '自运' && contracts.length) { if (this.dialogForm.carrierType === '自运' && contracts.length) {
this.taskCarrierOptions = contracts; this.taskCarrierOptions = contracts;
+1 -3
View File
@@ -2003,9 +2003,7 @@ export default {
getCustomerArchiveList(1, 100, { getCustomerArchiveList(1, 100, {
customerType: type, customerType: type,
status: 1, status: 1,
...(this.dialogType !== 'add' && type === '客户' approvalStatus: 'approved',
? { approvalStatus: 'approved' }
: {}),
}) })
.then(res => { .then(res => {
const records = (res.data.data && res.data.data.records) || []; const records = (res.data.data && res.data.data.records) || [];
+38
View File
@@ -0,0 +1,38 @@
<template>
<basic-container class="waybill-import-page-container">
<div class="waybill-import-page-title">导入运单</div>
<waybill-import-dialog standalone />
</basic-container>
</template>
<script setup>
import WaybillImportDialog from './components/waybill-import-dialog.vue';
</script>
<style scoped lang="scss">
.waybill-import-page-container {
:deep(.basic-container__card > .el-card__body) {
padding: 0;
}
}
.waybill-import-page-title {
position: relative;
padding: 16px 24px 12px 36px;
border-bottom: 1px solid #eff1f7;
color: #303133;
font-size: 18px;
font-weight: 600;
&::before {
position: absolute;
top: 16px;
left: 24px;
width: 4px;
height: 22px;
border-radius: 2px;
background: #409eff;
content: '';
}
}
</style>
+10 -6
View File
@@ -1403,6 +1403,14 @@ export default {
attachmentFileName(file) { attachmentFileName(file) {
return String(file?.originalName || file?.name || file?.fileName || '').trim(); return String(file?.originalName || file?.name || file?.fileName || '').trim();
}, },
normalizedAttachmentFileName(file) {
let fileName = this.attachmentFileName(file) || this.attachmentFileUrl(file);
fileName = String(fileName || '').split(/[?#]/)[0];
try {
fileName = decodeURIComponent(fileName);
} catch {}
return fileName.toLocaleLowerCase();
},
attachmentFileUrl(file) { attachmentFileUrl(file) {
return ( return (
file?.url || file?.url ||
@@ -1469,7 +1477,7 @@ export default {
this.$message.error('附件预览失败'); this.$message.error('附件预览失败');
}, },
attachmentMatchesMaterial(file, material) { attachmentMatchesMaterial(file, material) {
const fileName = this.attachmentFileName(file).toLocaleLowerCase(); const fileName = this.normalizedAttachmentFileName(file);
return ( return (
Boolean(this.attachmentFileUrl(file)) && Boolean(this.attachmentFileUrl(file)) &&
(file.attachmentType === MATERIAL_TYPE_MAP[material.key] || (file.attachmentType === MATERIAL_TYPE_MAP[material.key] ||
@@ -1477,11 +1485,7 @@ export default {
); );
}, },
resolveAttachmentTypeByFileName(file) { resolveAttachmentTypeByFileName(file) {
let fileName = this.attachmentFileName(file) || this.attachmentFileUrl(file); const fileName = this.normalizedAttachmentFileName(file);
fileName = String(fileName || '').split(/[?#]/)[0].toLocaleLowerCase();
try {
fileName = decodeURIComponent(fileName);
} catch {}
const material = [...Object.values(ATTACHMENT_MATERIALS)] const material = [...Object.values(ATTACHMENT_MATERIALS)]
.sort( .sort(
(a, b) => (a, b) =>
+5 -26
View File
@@ -169,8 +169,8 @@
<script> <script>
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue'; import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import * as XLSX from 'xlsx';
import { createSettlementTransfer } from '@/utils/settlement-transfer'; import { createSettlementTransfer } from '@/utils/settlement-transfer';
import { downloadFile } from '@/utils/util';
import * as api from '@/api/settlement/formalSettlement'; import * as api from '@/api/settlement/formalSettlement';
import * as paymentApi from '@/api/payment/paymentApplication'; import * as paymentApi from '@/api/payment/paymentApplication';
import { import {
@@ -503,31 +503,10 @@ export default {
win.document.close(); win.document.close();
}, },
async handleExport() { async handleExport() {
const response = await api.getList(1, 100000, this.buildQueryParams()); const params = this.buildQueryParams();
const data = this.unwrapData(response); if (this.selection.length) params.ids = this.selection.map(item => item.id).join(',');
const rows = (data.records || []).map(item => ({ const { data } = await api.exportList(params);
结算单号: item.formalSettlementNo, downloadFile(data, `正式结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
预结算单号: item.preSettlementNos,
来源: item.sourceType,
付款方: item.payerName,
收款方: item.payeeName,
项目名称: item.projectName,
所属组织: item.deptName,
合同编号: item.contractNo,
合同名称: item.contractName,
原币结算金额: item.settlementAmount,
本位币结算金额: item.localSettlementAmount,
结算汇率: item.exchangeRate,
发票状态: this.invoiceName(item.invoiceStatus, item.settlementType),
收付款状态: this.paymentName(item.paymentStatus),
审核状态: item.approvalStatusName,
金蝶单据号: item.kingdeeBillNo,
创建人: item.createUserName,
创建时间: item.createTime,
}));
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, XLSX.utils.json_to_sheet(rows), '正式结算单');
XLSX.writeFile(workbook, `正式结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
}, },
buildQueryParams() { buildQueryParams() {
// 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显) // 所属组织级联返回 id 路径,转成组织名称传给后端(用副本,避免污染搜索框回显)
+3 -1
View File
@@ -622,7 +622,9 @@ export default {
)}</div></div><h2>结算合计</h2><table><thead><tr><th>序号</th><th>费用类型</th><th>费用项</th><th>原金额</th><th>调整金额</th><th>结算金额</th></tr></thead><tbody>${summaryRows}</tbody></table><div class="actions"><button onclick="window.print()">打印 / 另存为 PDF</button></div></body></html>`; )}</div></div><h2>结算合计</h2><table><thead><tr><th>序号</th><th>费用类型</th><th>费用项</th><th>原金额</th><th>调整金额</th><th>结算金额</th></tr></thead><tbody>${summaryRows}</tbody></table><div class="actions"><button onclick="window.print()">打印 / 另存为 PDF</button></div></body></html>`;
}, },
async handleExport() { async handleExport() {
const { data } = await exportList(this.buildQuery()); const params = this.buildQuery();
if (this.selection.length) params.ids = this.selection.map(item => item.id).join(',');
const { data } = await exportList(params);
downloadFile(data, `预结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`); downloadFile(data, `预结算单${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
}, },
openFlow(row) { openFlow(row) {
@@ -93,7 +93,7 @@
<script> <script>
import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue'; import { ArrowDown, ArrowUp, Refresh } from '@element-plus/icons-vue';
import { mapGetters } from 'vuex'; import { mapGetters } from 'vuex';
import * as XLSX from 'xlsx'; import { downloadFile } from '@/utils/util';
import * as api from '@/api/settlement/transportReconciliation'; import * as api from '@/api/settlement/transportReconciliation';
import { transportReconciliationSearchFields } from '@/option/settlement/transportReconciliationSearch'; import { transportReconciliationSearchFields } from '@/option/settlement/transportReconciliationSearch';
import { transportReconciliationTableColumns } from '@/option/settlement/transportReconciliationTable'; import { transportReconciliationTableColumns } from '@/option/settlement/transportReconciliationTable';
@@ -217,30 +217,13 @@ export default {
this.loadTable(); this.loadTable();
}, },
async handleExport() { async handleExport() {
const response = await api.getList(1, 100000, { const params = {
...this.query, ...this.normalizeOrganizationSearch({ ...this.query }, 'deptName'),
settlementType: this.settlementType, settlementType: this.settlementType,
}); };
const data = response?.data?.data || response?.data || response || {}; if (this.selection.length) params.ids = this.selection.map(item => item.id).join(',');
const exportRows = (data.records || []).map(item => ({ const { data } = await api.exportList(params);
对账单号: item.reconciliationNo, downloadFile(data, `运输对账${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
付款方: item.payerName,
收款方: item.payeeName,
项目名称: item.projectName,
所属组织: item.deptName,
合同编号: item.contractNo,
合同名称: item.contractName,
结算金额: Number(item.settlementAmount || 0).toFixed(2),
对账模式: item.reconciliationModeName,
账单总数: item.externalBillCount,
匹配数: item.matchedCount,
对账状态: item.reconciliationStatusName,
创建人: item.createUserName,
创建时间: item.createTime,
}));
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, XLSX.utils.json_to_sheet(exportRows), '运输对账');
XLSX.writeFile(workbook, `运输对账${this.$dayjs().format('YYYY-MM-DD HH-mm-ss')}.xlsx`);
}, },
isEditable(row) { isEditable(row) {
return row.reconciliationStatus === 'unfinished'; return row.reconciliationStatus === 'unfinished';