parent
8a2fe85238
commit
6da9914114
@ -0,0 +1,381 @@
|
||||
<template>
|
||||
<div class="order-ledger-container">
|
||||
<el-card shadow="never" class="main-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="header-title">订单管理台账</span>
|
||||
<el-button size="default" type="primary" icon="ArrowLeft" @click="handleBack">返回</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="custom-tabs">
|
||||
<!-- 合同信息 Tab -->
|
||||
<el-tab-pane label="合同信息" name="contract">
|
||||
<div v-loading="loadingContract" class="tab-content">
|
||||
<el-descriptions :column="3" border v-if="contractInfo" class="order-descriptions">
|
||||
<el-descriptions-item label="合同编号" :span="1">
|
||||
<span class="value-text value-highlight">{{ contractInfo?.contractCode || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同名称" :span="2">
|
||||
<span class="value-text value-highlight">{{ contractInfo?.contractName || '-' }}</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="客户合同编号">{{ contractInfo?.customerContractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="内部合同号">{{ contractInfo?.internalContractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="外部合同号">{{ contractInfo?.externalContractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="订单号">{{ contractInfo?.orderContractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目号">{{ contractInfo?.projectContractCode || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="业务方向">
|
||||
<dict-tag :options="business_direction" :value="contractInfo?.businessDirection" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同大类">
|
||||
<dict-tag :options="contract_category" :value="contractInfo?.contractCategory" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同类型">
|
||||
<dict-tag :options="contract_type" :value="contractInfo?.contractType" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同状态">
|
||||
<dict-tag :options="contract_status" :value="contractInfo?.contractStatus" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="合同时间">{{ contractInfo?.contractDate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="合同总价" :span="1">
|
||||
<span class="value-text value-amount">
|
||||
{{ contractInfo?.totalPrice ? formatNumber(contractInfo.totalPrice) + ' 元' : '-' }}
|
||||
</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="付款方式">{{ contractInfo?.paymentMethod || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交货地点">{{ contractInfo?.deliveryLocation || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="运输方式">{{ contractInfo?.shipMethod || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="交付启动期限">{{
|
||||
contractInfo?.deliveryStart ? contractInfo.deliveryStart + ' 天' : '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="质保期">{{
|
||||
contractInfo?.warrantyPeriod ? contractInfo.warrantyPeriod + ' 天' : '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="质保期描述">{{ contractInfo?.warrantyPeriodDescription || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="签订地点">{{ contractInfo?.signingPlace || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="3">{{ contractInfo?.remark || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-empty v-else description="暂无合同信息" class="empty-state" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 发货信息 Tab -->
|
||||
<el-tab-pane label="发货信息" name="shipping">
|
||||
<div v-loading="loadingShipping" class="tab-content">
|
||||
<el-table :data="shippingList" border stripe size="default" class="data-table">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column label="发货单号" prop="shippingCode" width="150" align="center" />
|
||||
<el-table-column label="发货方式" prop="shippingMode" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="shipping_mode" :value="scope.row.shippingMode" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="客户名称" prop="customerName" width="180" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="收货地址" prop="shippingAddress" width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="收货联系人" prop="receiverName" width="100" align="center" />
|
||||
<el-table-column label="收货电话" prop="receiverPhone" width="130" align="center" />
|
||||
<!-- <el-table-column label="物流公司" prop="logisticsCompany" width="130" align="center" />-->
|
||||
<!-- <el-table-column label="运单号" prop="trackingNo" width="150" align="center" />-->
|
||||
<el-table-column label="发货状态" prop="shippingStatus" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="shipping_status" :value="scope.row.shippingStatus" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="计划到货时间" prop="planArrivalTime" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.planArrivalTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际发货时间" prop="shippingTime" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.shippingTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发货说明" prop="directions" min-width="200" align="center" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="100">
|
||||
<template #default="scope">
|
||||
<el-tooltip content="查看发货单明细" placement="top">
|
||||
<el-button link type="primary" icon="View" @click="handleViewShipping(scope.row)">明细</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-empty v-if="!loadingShipping && shippingList.length === 0" description="暂无发货信息" class="empty-state" />
|
||||
<pagination
|
||||
v-show="shippingTotal > 0"
|
||||
:total="shippingTotal"
|
||||
v-model:page="shippingQuery.pageNum"
|
||||
v-model:limit="shippingQuery.pageSize"
|
||||
@pagination="loadShippingList"
|
||||
class="pagination-wrapper"
|
||||
/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 回款信息 Tab -->
|
||||
<el-tab-pane label="回款信息" name="payment">
|
||||
<div v-loading="loadingPayment" class="tab-content">
|
||||
<el-table :data="planStageList" border stripe size="default" class="data-table">
|
||||
<el-table-column label="序号" type="index" width="60" align="center" />
|
||||
<el-table-column label="回款阶段" width="220" align="center">
|
||||
<template #default="scope">
|
||||
{{ getPaymentStageName(scope.row.collectionStage) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预计回款比例(%)" width="160" align="center" prop="repaymentRate" />
|
||||
<el-table-column label="预计回款时间" width="150" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ parseTime(scope.row.repaymentTime, '{y}-{m}-{d}') || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" min-width="200" prop="remark" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<el-empty v-if="!loadingPayment && planStageList.length === 0" description="暂无回款信息" class="empty-state" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 开票信息 Tab -->
|
||||
<el-tab-pane label="开票信息" name="invoice">
|
||||
<div class="tab-content">
|
||||
<el-empty description="暂无开票信息,功能开发中" class="empty-state" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="OrderLedger">
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getContractOrder } from '@/api/oa/erp/contractOrder';
|
||||
import { getContractInfo } from '@/api/oa/erp/contractInfo';
|
||||
import { ContractInfoVO } from '@/api/oa/erp/contractInfo/types';
|
||||
import { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
|
||||
import { listWmsShippingBill } from '@/api/wms/wmsShippingBill';
|
||||
import { WmsShippingBillVO } from '@/api/wms/wmsShippingBill/types';
|
||||
import { getErpProjectPlanStageList } from '@/api/oa/erp/erpProjectPlanStage';
|
||||
import { ErpProjectPlanStageForm } from '@/api/oa/erp/erpProjectPlanStage/types';
|
||||
import { getBasePaymentStageList } from '@/api/oa/base/paymentStage';
|
||||
import { PaymentStageVO } from '@/api/oa/base/paymentStage/types';
|
||||
|
||||
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const { business_direction, contract_category, contract_type, contract_status, shipping_mode, shipping_status } = toRefs<any>(
|
||||
proxy?.useDict('business_direction', 'contract_category', 'contract_type', 'contract_status', 'shipping_mode', 'shipping_status')
|
||||
);
|
||||
|
||||
const projectId = computed(() => route.params.projectId as string | number | undefined);
|
||||
const orderInfo = ref<ProjectInfoVO | null>(null);
|
||||
const contractInfo = ref<ContractInfoVO | null>(null);
|
||||
const shippingList = ref<WmsShippingBillVO[]>([]);
|
||||
const planStageList = ref<ErpProjectPlanStageForm[]>([]);
|
||||
const paymentStageList = ref<PaymentStageVO[]>([]);
|
||||
|
||||
const activeTab = ref('contract');
|
||||
const loadedTabs = ref<Set<string>>(new Set(['contract']));
|
||||
|
||||
const loadingContract = ref(false);
|
||||
const loadingShipping = ref(false);
|
||||
const loadingPayment = ref(false);
|
||||
|
||||
const shippingTotal = ref(0);
|
||||
const shippingQuery = reactive({ pageNum: 1, pageSize: 10 });
|
||||
|
||||
/** 格式化数字 */
|
||||
const formatNumber = (num: number) => {
|
||||
return num?.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
};
|
||||
|
||||
/** 返回 */
|
||||
const handleBack = () => {
|
||||
proxy?.$tab.closePage(route);
|
||||
router.back();
|
||||
};
|
||||
|
||||
/** 查看发货单明细 */
|
||||
const handleViewShipping = (row: WmsShippingBillVO) => {
|
||||
router.push({ path: '/shipping/wmsShippingBill/edit', query: { type: 'view', id: row.shippingBillId as string } });
|
||||
};
|
||||
|
||||
/** 加载订单基本信息和合同信息 */
|
||||
const loadOrderAndContract = async () => {
|
||||
if (!projectId.value) return;
|
||||
loadingContract.value = true;
|
||||
try {
|
||||
// 先获取订单信息
|
||||
const orderRes = await getContractOrder(projectId.value);
|
||||
orderInfo.value = orderRes.data;
|
||||
// 再通过 contractId 获取合同详情
|
||||
if (orderRes.data?.contractId) {
|
||||
const contractRes = await getContractInfo(orderRes.data.contractId);
|
||||
contractInfo.value = contractRes.data;
|
||||
}
|
||||
} finally {
|
||||
loadingContract.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 加载发货信息 */
|
||||
const loadShippingList = async () => {
|
||||
if (!orderInfo.value?.contractId) {
|
||||
shippingList.value = [];
|
||||
shippingTotal.value = 0;
|
||||
return;
|
||||
}
|
||||
loadingShipping.value = true;
|
||||
try {
|
||||
const res: any = await listWmsShippingBill({
|
||||
...shippingQuery,
|
||||
contractId: orderInfo.value.contractId
|
||||
});
|
||||
shippingList.value = res.rows || [];
|
||||
shippingTotal.value = res.total || 0;
|
||||
} finally {
|
||||
loadingShipping.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 加载回款阶段基础数据 */
|
||||
const loadPaymentStageBaseList = async () => {
|
||||
try {
|
||||
const res = await getBasePaymentStageList({ pageNum: 1, pageSize: 9999 });
|
||||
paymentStageList.value = (res.data || []).map((item: any) => ({
|
||||
...item,
|
||||
paymentStageId: item.paymentStageId != null ? String(item.paymentStageId) : item.paymentStageId
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('查询回款阶段列表失败:', error);
|
||||
paymentStageList.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
/** 加载回款信息(项目阶段计划) */
|
||||
const loadPlanStageList = async () => {
|
||||
if (!projectId.value) {
|
||||
planStageList.value = [];
|
||||
return;
|
||||
}
|
||||
loadingPayment.value = true;
|
||||
try {
|
||||
const res = await getErpProjectPlanStageList({ projectId: projectId.value });
|
||||
planStageList.value = (res.data || []).map((item: any) => ({
|
||||
...item,
|
||||
collectionStage: item.collectionStage || '',
|
||||
repaymentRate: item.repaymentRate || undefined,
|
||||
repaymentTime: item.repaymentTime || undefined,
|
||||
remark: item.remark || ''
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('查询回款信息失败:', error);
|
||||
planStageList.value = [];
|
||||
} finally {
|
||||
loadingPayment.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
/** 获取回款阶段名称 */
|
||||
const getPaymentStageName = (stageId: string | number) => {
|
||||
if (!stageId) return '-';
|
||||
const stage = paymentStageList.value.find((s) => String(s.paymentStageId) === String(stageId));
|
||||
return stage?.paymentMethod || '-';
|
||||
};
|
||||
|
||||
/** Tab 切换 */
|
||||
const handleTabChange = (tabName: string) => {
|
||||
if (!loadedTabs.value.has(tabName)) {
|
||||
loadedTabs.value.add(tabName);
|
||||
switch (tabName) {
|
||||
case 'shipping':
|
||||
shippingQuery.pageNum = 1;
|
||||
loadShippingList();
|
||||
break;
|
||||
case 'payment':
|
||||
loadPlanStageList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => projectId.value,
|
||||
() => {
|
||||
if (projectId.value) {
|
||||
loadedTabs.value.clear();
|
||||
loadedTabs.value.add('contract');
|
||||
loadPaymentStageBaseList();
|
||||
loadOrderAndContract();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.order-ledger-container {
|
||||
padding: 16px;
|
||||
|
||||
.main-card {
|
||||
:deep(.el-card__header) {
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-tabs {
|
||||
:deep(.el-tabs__content) {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.order-descriptions {
|
||||
.value-text {
|
||||
&.value-highlight {
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
&.value-amount {
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-table {
|
||||
:deep(.el-table__header) {
|
||||
th {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue