|
|
@@ -2,167 +2,274 @@
|
|
|
<div class="page-container">
|
|
|
<PageTitle title="对账结算状况" />
|
|
|
|
|
|
- <SearchBar :form="searchForm" :filters="filters">
|
|
|
- <template #buttons>
|
|
|
- <el-button type="danger">导出</el-button>
|
|
|
- </template>
|
|
|
- </SearchBar>
|
|
|
-
|
|
|
- <el-table :data="tableData" border style="width: 100%">
|
|
|
- <el-table-column prop="orderNo" label="订单编号" min-width="110" align="center" />
|
|
|
- <el-table-column prop="orderType" label="订单类型" min-width="90" align="center">
|
|
|
- <template #default="{ row }">{{ row.orderType || '-' }}</template>
|
|
|
+ <div class="filter-bar">
|
|
|
+ <el-form :model="searchForm" inline>
|
|
|
+ <el-form-item label="对账状态">
|
|
|
+ <el-select v-model="searchForm.billStatus" placeholder="请选择对账状态" clearable @change="handleSearch">
|
|
|
+ <el-option v-for="item in billStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="开票状态">
|
|
|
+ <el-select v-model="searchForm.invoiceStatus" placeholder="请选择开票状态" clearable @change="handleSearch">
|
|
|
+ <el-option v-for="item in invoiceStatusOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" @click="handleSearch">搜索</el-button>
|
|
|
+ <el-button @click="handleReset">重置</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="chart-section">
|
|
|
+ <div class="chart-header">
|
|
|
+ <span class="chart-title">对账金额趋势</span>
|
|
|
+ </div>
|
|
|
+ <div class="chart-container" ref="chartRef"></div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-table v-loading="loading" :data="tableData" border style="width: 100%">
|
|
|
+ <el-table-column prop="billNo" label="对账编号" min-width="130" align="center" />
|
|
|
+ <el-table-column prop="billDate" label="对账日期" min-width="110" align="center">
|
|
|
+ <template #default="{ row }">{{ parseTime(row.billDate, '{y}-{m}-{d}') }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="amount" label="金额" min-width="100" align="center">
|
|
|
- <template #default="{ row }">¥{{ row.amount.toLocaleString() }}</template>
|
|
|
+ <el-table-column prop="amount" label="对账单金额" min-width="110" align="center">
|
|
|
+ <template #default="{ row }">¥{{ row.amount.toFixed(2) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="orderDate" label="下单日期" min-width="100" align="center" />
|
|
|
- <el-table-column prop="orderStatus" label="订单状态" min-width="90" align="center">
|
|
|
+ <el-table-column prop="billStatus" label="对账状态" min-width="90" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <span :class="['status-tag', getOrderStatusClass(row.orderStatus)]">{{ row.orderStatus }}</span>
|
|
|
+ {{ getDictLabel(statement_status, row.billStatus) }}
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="billStatus" label="对账状态" min-width="90" align="center">
|
|
|
- <template #default="{ row }">{{ row.billStatus || '-' }}</template>
|
|
|
- </el-table-column>
|
|
|
<el-table-column prop="invoiceStatus" label="开票状态" min-width="90" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <span :class="['status-tag', getInvoiceStatusClass(row.invoiceStatus)]">{{ row.invoiceStatus }}</span>
|
|
|
+ <span :style="{ color: row.invoiceStatus === '0' ? '#e60012' : '' }">
|
|
|
+ {{ getDictLabel(invoice_issuance_status, row.invoiceStatus) }}
|
|
|
+ </span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="settleStatus" label="结算状态" min-width="90" align="center">
|
|
|
+ <!-- <el-table-column prop="payStatus" label="支付状态" min-width="90" align="center">
|
|
|
<template #default="{ row }">
|
|
|
- <span :style="{ color: row.settleStatus === '已结算' ? '#67c23a' : '' }">{{ row.settleStatus }}</span>
|
|
|
+ {{ getDictLabel(payment_status, row.payStatus) }}
|
|
|
</template>
|
|
|
- </el-table-column>
|
|
|
+ </el-table-column> -->
|
|
|
</el-table>
|
|
|
|
|
|
- <TablePagination v-model:page="pagination.page" v-model:pageSize="pagination.pageSize" :total="pagination.total" />
|
|
|
+ <TablePagination v-model:page="pagination.page" v-model:pageSize="pagination.pageSize" :total="pagination.total" @change="loadStatementList" />
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { reactive, ref } from 'vue';
|
|
|
-import { PageTitle, SearchBar, TablePagination } from '@/components';
|
|
|
-import { BILL_STATUS_OPTIONS, INVOICE_STATUS_OPTIONS, SETTLE_STATUS_OPTIONS } from '@/constants/status';
|
|
|
-import { getOrderStatusClass, getInvoiceStatusClass } from '@/utils/status';
|
|
|
+import { reactive, ref, onMounted, computed, watch, nextTick, getCurrentInstance, toRefs } from 'vue';
|
|
|
+import { ElMessage } from 'element-plus';
|
|
|
+import { PageTitle, TablePagination } from '@/components';
|
|
|
+import { getStatementList } from '@/api/pc/enterprise/statement';
|
|
|
+import type { StatementOrder } from '@/api/pc/enterprise/statementTypes';
|
|
|
+import type { ComponentInternalInstance } from 'vue';
|
|
|
+import * as echarts from 'echarts';
|
|
|
+
|
|
|
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
+const { invoice_issuance_status, statement_status, payment_status } = toRefs<any>(
|
|
|
+ proxy?.useDict('invoice_issuance_status', 'statement_status', 'payment_status')
|
|
|
+);
|
|
|
+
|
|
|
+const billStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(statement_status.value || [])]);
|
|
|
+const invoiceStatusOptions = computed(() => [{ label: '全部', value: '' }, ...(invoice_issuance_status.value || [])]);
|
|
|
|
|
|
+const loading = ref(false);
|
|
|
const searchForm = reactive({
|
|
|
- keyword: '',
|
|
|
- dateRange: [],
|
|
|
billStatus: '',
|
|
|
- invoiceStatus: '',
|
|
|
- settleStatus: ''
|
|
|
+ invoiceStatus: ''
|
|
|
});
|
|
|
|
|
|
-const filters = [
|
|
|
- { field: 'billStatus', label: '对账状态', options: BILL_STATUS_OPTIONS },
|
|
|
- { field: 'invoiceStatus', label: '开票状态', options: INVOICE_STATUS_OPTIONS },
|
|
|
- { field: 'settleStatus', label: '结算状态', options: SETTLE_STATUS_OPTIONS }
|
|
|
-];
|
|
|
-
|
|
|
-const pagination = reactive({ page: 1, pageSize: 10, total: 100 });
|
|
|
-
|
|
|
-const tableData = ref([
|
|
|
- {
|
|
|
- orderNo: '232323232',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 2115.97,
|
|
|
- orderDate: '2025-11-22',
|
|
|
- orderStatus: '运输中',
|
|
|
- billStatus: '已对账',
|
|
|
- invoiceStatus: '已付款',
|
|
|
- settleStatus: '已结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323412123',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 4476.12,
|
|
|
- orderDate: '2025-12-09',
|
|
|
- orderStatus: '待发货',
|
|
|
- billStatus: '未对账',
|
|
|
- invoiceStatus: '待开票',
|
|
|
- settleStatus: '未结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '4566784543',
|
|
|
- orderType: '协议订单',
|
|
|
- amount: 7751.34,
|
|
|
- orderDate: '2025-11-21',
|
|
|
- orderStatus: '已收货',
|
|
|
- billStatus: '已对账',
|
|
|
- invoiceStatus: '已开票',
|
|
|
- settleStatus: '已结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2356565654',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 7936.37,
|
|
|
- orderDate: '2025-11-27',
|
|
|
- orderStatus: '运输中',
|
|
|
- billStatus: '未对账',
|
|
|
- invoiceStatus: '已付款',
|
|
|
- settleStatus: '未结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323412123',
|
|
|
- orderType: '协议订单',
|
|
|
- amount: 3931.45,
|
|
|
- orderDate: '2025-12-11',
|
|
|
- orderStatus: '待发货',
|
|
|
- billStatus: '未对账',
|
|
|
- invoiceStatus: '待开票',
|
|
|
- settleStatus: '未结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323232323',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 6132.75,
|
|
|
- orderDate: '2025-11-27',
|
|
|
- orderStatus: '已收货',
|
|
|
- billStatus: '已对账',
|
|
|
- invoiceStatus: '已开票',
|
|
|
- settleStatus: '已结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2356565654',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 3189.56,
|
|
|
- orderDate: '2025-11-25',
|
|
|
- orderStatus: '已收货',
|
|
|
- billStatus: '已对账',
|
|
|
- invoiceStatus: '已开票',
|
|
|
- settleStatus: '已结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323232323',
|
|
|
- orderType: '协议订单',
|
|
|
- amount: 993.66,
|
|
|
- orderDate: '2025-11-20',
|
|
|
- orderStatus: '运输中',
|
|
|
- billStatus: '未对账',
|
|
|
- invoiceStatus: '已付款',
|
|
|
- settleStatus: '未结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323412123',
|
|
|
- orderType: '普通订单',
|
|
|
- amount: 2763.35,
|
|
|
- orderDate: '2025-11-17',
|
|
|
- orderStatus: '待发货',
|
|
|
- billStatus: '未对账',
|
|
|
- invoiceStatus: '待开票',
|
|
|
- settleStatus: '未结算'
|
|
|
- },
|
|
|
- {
|
|
|
- orderNo: '2323232323',
|
|
|
- orderType: '协议订单',
|
|
|
- amount: 9286.45,
|
|
|
- orderDate: '2025-12-05',
|
|
|
- orderStatus: '已收货',
|
|
|
- billStatus: '已对账',
|
|
|
- invoiceStatus: '已开票',
|
|
|
- settleStatus: '已结算'
|
|
|
+const pagination = reactive({ page: 1, pageSize: 10, total: 0 });
|
|
|
+const tableData = ref<any[]>([]);
|
|
|
+
|
|
|
+const chartRef = ref<HTMLElement>();
|
|
|
+let chartInstance: echarts.ECharts | null = null;
|
|
|
+
|
|
|
+const chartData = computed(() => {
|
|
|
+ if (!tableData.value.length) return { dates: [], amounts: [] };
|
|
|
+
|
|
|
+ const dailyMap: Record<string, number> = {};
|
|
|
+ tableData.value.forEach((row: any) => {
|
|
|
+ const date = (row.billDate || '').split(' ')[0];
|
|
|
+ if (!date) return;
|
|
|
+ if (!dailyMap[date]) dailyMap[date] = 0;
|
|
|
+ dailyMap[date] += Number(row.amount) || 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ const sorted = Object.entries(dailyMap).sort(([a], [b]) => a.localeCompare(b));
|
|
|
+ return {
|
|
|
+ dates: sorted.map(([date]) => date),
|
|
|
+ amounts: sorted.map(([, v]) => v)
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+const initLineChart = () => {
|
|
|
+ if (!chartRef.value) return;
|
|
|
+ if (!chartInstance) chartInstance = echarts.init(chartRef.value);
|
|
|
+
|
|
|
+ const { dates, amounts } = chartData.value;
|
|
|
+ const hasData = dates.length > 0;
|
|
|
+ const maxVal = hasData ? Math.max(...amounts) : 0;
|
|
|
+
|
|
|
+ chartInstance.setOption(
|
|
|
+ {
|
|
|
+ grid: { left: 60, right: 20, top: 30, bottom: 30 },
|
|
|
+ xAxis: {
|
|
|
+ type: 'category',
|
|
|
+ data: dates,
|
|
|
+ axisLine: { lineStyle: { color: '#eee' } },
|
|
|
+ axisLabel: { color: '#999', fontSize: 11, rotate: dates.length > 10 ? 45 : 0 },
|
|
|
+ axisTick: { alignWithLabel: true }
|
|
|
+ },
|
|
|
+ yAxis: {
|
|
|
+ type: 'value',
|
|
|
+ name: '金额(元)',
|
|
|
+ nameTextStyle: { color: '#999', fontSize: 12 },
|
|
|
+ axisLine: { show: false },
|
|
|
+ splitLine: { lineStyle: { color: '#f0f0f0', type: 'dashed' } },
|
|
|
+ axisLabel: {
|
|
|
+ color: '#999',
|
|
|
+ formatter: (v: number) => (v >= 10000 ? (v / 10000).toFixed(1) + '万' : v)
|
|
|
+ },
|
|
|
+ max: maxVal > 0 ? Math.ceil(maxVal * 1.15) : undefined
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '对账单金额',
|
|
|
+ type: 'line',
|
|
|
+ data: amounts,
|
|
|
+ smooth: true,
|
|
|
+ lineStyle: { color: '#e60012', width: 2.5 },
|
|
|
+ itemStyle: { color: '#e60012', borderWidth: 2, borderColor: '#fff' },
|
|
|
+ symbol: 'circle',
|
|
|
+ symbolSize: 6,
|
|
|
+ areaStyle: {
|
|
|
+ color: {
|
|
|
+ type: 'linear',
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ x2: 0,
|
|
|
+ y2: 1,
|
|
|
+ colorStops: [
|
|
|
+ { offset: 0, color: 'rgba(230, 0, 18, 0.15)' },
|
|
|
+ { offset: 1, color: 'rgba(230, 0, 18, 0.01)' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ emphasis: { symbolSize: 10, itemStyle: { borderWidth: 3 } }
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'axis',
|
|
|
+ backgroundColor: '#fff',
|
|
|
+ borderColor: '#eee',
|
|
|
+ borderWidth: 1,
|
|
|
+ textStyle: { color: '#333', fontSize: 12 },
|
|
|
+ formatter: (params: any) => {
|
|
|
+ const p = params[0];
|
|
|
+ return `<div style="font-weight:bold;margin-bottom:4px">${p.name}</div>金额: <span style="color:#e60012;font-weight:bold">¥${p.value.toLocaleString('en-US', { minimumFractionDigits: 2 })}</span>`;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ animationDuration: 800,
|
|
|
+ animationEasing: 'cubicOut'
|
|
|
+ },
|
|
|
+ { notMerge: true }
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+watch(
|
|
|
+ () => tableData.value,
|
|
|
+ () => nextTick(() => initLineChart()),
|
|
|
+ { deep: true }
|
|
|
+);
|
|
|
+
|
|
|
+const getDictLabel = (dictOptions: any[], value: string) => {
|
|
|
+ if (!dictOptions || !value) return value;
|
|
|
+ const dict = dictOptions.find((item) => item.value === value);
|
|
|
+ return dict ? dict.label : value;
|
|
|
+};
|
|
|
+
|
|
|
+const loadStatementList = async () => {
|
|
|
+ try {
|
|
|
+ loading.value = true;
|
|
|
+ const queryParams = {
|
|
|
+ pageNum: pagination.page,
|
|
|
+ pageSize: pagination.pageSize,
|
|
|
+ statementStatus: searchForm.billStatus,
|
|
|
+ isInvoiceStatus: searchForm.invoiceStatus
|
|
|
+ };
|
|
|
+
|
|
|
+ const res = await getStatementList(queryParams);
|
|
|
+
|
|
|
+ if (res.code === 200 && res.rows) {
|
|
|
+ tableData.value = res.rows.map((item: StatementOrder) => ({
|
|
|
+ id: item.id,
|
|
|
+ billNo: item.statementOrderNo,
|
|
|
+ billDate: item.statementDate,
|
|
|
+ amount: parseFloat(item.amount as any) || 0,
|
|
|
+ billStatus: item.statementStatus,
|
|
|
+ invoiceStatus: item.isInvoiceStatus,
|
|
|
+ payStatus: item.isPaymentStatus
|
|
|
+ }));
|
|
|
+ pagination.total = res.total || 0;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载对账单列表失败:', error);
|
|
|
+ ElMessage.error('加载对账单列表失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
-]);
|
|
|
+};
|
|
|
+
|
|
|
+const handleSearch = () => {
|
|
|
+ pagination.page = 1;
|
|
|
+ loadStatementList();
|
|
|
+};
|
|
|
+
|
|
|
+const handleReset = () => {
|
|
|
+ searchForm.billStatus = '';
|
|
|
+ searchForm.invoiceStatus = '';
|
|
|
+ pagination.page = 1;
|
|
|
+ loadStatementList();
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadStatementList();
|
|
|
+});
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped lang="scss">
|
|
|
+.filter-bar {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 12px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+}
|
|
|
+.chart-section {
|
|
|
+ background: #fff;
|
|
|
+ border: 1px solid #eee;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+
|
|
|
+ .chart-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .chart-title {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: 280px;
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|