feat: 对账管理

This commit is contained in:
2026-07-27 11:33:43 +08:00
parent d4e7e4282b
commit 2d8a31a3cc
9 changed files with 995 additions and 0 deletions
@@ -0,0 +1,448 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="pageForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</search-item>
<search-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="pageForm.orderId"></el-input>
</search-item>
<search-item label="用户ID">
<el-input clearable placeholder="请输入用户ID" v-model="pageForm.userId"></el-input>
</search-item>
<search-item label="起始日期">
<el-date-picker clearable placeholder="开始日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.startDate"></el-date-picker>
</search-item>
<search-item label="结束日期">
<el-date-picker clearable placeholder="结束日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.endDate"></el-date-picker>
</search-item>
<search-item label="对账状态">
<el-select clearable placeholder="请选择对账状态" style="width: 100%" v-model="pageForm.reconciliationStatus">
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in reconciliationStatusOptions"></el-option>
</el-select>
</search-item>
<search-item label="发票状态">
<el-select clearable placeholder="请选择发票状态" style="width: 100%" v-model="pageForm.invoiceStatus">
<el-option :key="item.value" :label="item.label" :value="item.value" v-for="item in invoiceStatusOptions"></el-option>
</el-select>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="对账订单列表">
<el-button @click="openReconcileDialog" size="mini" type="success">对账</el-button>
<el-button @click="openRecordDialog" size="mini" type="primary">对账记录</el-button>
</table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单ID" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商编码" min-width="120" prop="supplierId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="下单时间" min-width="160" prop="orderCreateTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="完成时间" min-width="160" prop="orderCompleteTime" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="orderStatusType(row.orderState)" size="mini">{{ orderStateName(row.orderState) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="对账状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="reconciliationStatusType(row.reconciliationStatus)" size="mini">{{ reconciliationStatusName(row.reconciliationStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="发票状态" min-width="110">
<template slot-scope="{row}">
<el-tag :type="invoiceStatusType(row.invoiceStatus)" size="mini">{{ invoiceStatusName(row.invoiceStatus) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="订单总金额" min-width="110" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="现金金额" min-width="110" prop="wPayOrAPay"></el-table-column>
<el-table-column align="center" label="积分金额" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="退款积分" min-width="110" prop="refund"></el-table-column>
<el-table-column align="center" label="发票代码" min-width="130" prop="invoiceCode" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="90">
<template slot-scope="{row}">
<el-button @click="showOrderDetail(row)" size="mini">详情</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="确认对账" :visible.sync="reconcileDialogVisible" width="480px">
<el-form label-width="100px" size="small">
<el-form-item label="供应商">
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="reconcileForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="对账类型">
<el-select placeholder="请选择对账类型" style="width: 100%" v-model="reconcileForm.reconciliationType">
<el-option label="正常订单对账" :value="1"></el-option>
<el-option label="历史异常订单对账" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="起始日期">
<el-date-picker placeholder="开始日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="reconcileForm.startDate"></el-date-picker>
</el-form-item>
<el-form-item label="结束日期">
<el-date-picker placeholder="结束日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="reconcileForm.endDate"></el-date-picker>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="reconcileForm.orderId"></el-input>
</el-form-item>
<el-form-item label="积分合计">
<span>{{ reconcilePointsTotal }}</span>
</el-form-item>
</el-form>
<el-alert :closable="false" show-icon title="确认后订单将更新为已对账或对账异常,并生成对账记录。" type="warning"></el-alert>
<span slot="footer">
<el-button @click="reconcileDialogVisible = false">取消</el-button>
<el-button @click="loadPointsTotal" :loading="pointsTotalLoading">计算金额</el-button>
<el-button @click="executeReconciliation" :loading="executeLoading" type="primary">确认对账</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="对账记录" :visible.sync="recordDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="供应商">
<el-select clearable filterable placeholder="供应商" style="width: 180px" v-model="recordForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="订单ID" v-model="recordForm.orderId"></el-input>
</el-form-item>
<el-form-item label="用户ID">
<el-input clearable placeholder="用户ID" v-model="recordForm.userId"></el-input>
</el-form-item>
<el-form-item label="批次号">
<el-input clearable placeholder="批次号" v-model="recordForm.batchNo"></el-input>
</el-form-item>
<el-form-item label="差异类型">
<el-select clearable placeholder="差异类型" style="width: 130px" v-model="recordForm.diffType">
<el-option label="一致" :value="0"></el-option>
<el-option label="金额异常" :value="1"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="recordSearch" type="primary">查询</el-button>
<el-button @click="recordReset">重置</el-button>
</el-form-item>
</el-form>
<el-table :data="recordList" border size="small" style="width: 100%" v-loading="recordLoading">
<el-table-column :index="recordIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="批次号" min-width="160" prop="batchNo" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="订单ID" min-width="160" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="140" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="110" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="平台总金额" min-width="110" prop="pTotalPrice"></el-table-column>
<el-table-column align="center" label="供应商总金额" min-width="120" prop="sTotalPrice"></el-table-column>
<el-table-column align="center" label="平台积分" min-width="100" prop="pPointsPrice"></el-table-column>
<el-table-column align="center" label="供应商积分" min-width="110" prop="sPointsPrice"></el-table-column>
<el-table-column align="center" label="差异类型" min-width="100">
<template slot-scope="{row}">
<el-tag :type="row.diffType === 0 ? 'success' : 'danger'" size="mini">{{ diffTypeName(row.diffType) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="差异原因" min-width="220" prop="diffReason" show-overflow-tooltip></el-table-column>
</el-table>
<el-pagination
:current-page="recordForm.pageNumber"
:page-size="recordForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="recordForm.totalCount"
@current-change="recordPageChange"
@size-change="recordSizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="recordDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="订单详情" :visible.sync="detailDialogVisible" width="760px">
<el-descriptions :column="2" border size="small" v-if="currentOrder">
<el-descriptions-item label="订单ID">{{ currentOrder.orderId || "-" }}</el-descriptions-item>
<el-descriptions-item label="订单编码">{{ currentOrder.orderNumberId || "-" }}</el-descriptions-item>
<el-descriptions-item label="供应商">{{ currentOrder.supplierName || currentOrder.supplierId || "-" }}</el-descriptions-item>
<el-descriptions-item label="用户ID">{{ currentOrder.userId || "-" }}</el-descriptions-item>
<el-descriptions-item label="下单时间">{{ currentOrder.orderCreateTime || "-" }}</el-descriptions-item>
<el-descriptions-item label="完成时间">{{ currentOrder.orderCompleteTime || "-" }}</el-descriptions-item>
<el-descriptions-item label="订单总金额">{{ currentOrder.totalPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="现金金额">{{ currentOrder.wPayOrAPay || "0" }}</el-descriptions-item>
<el-descriptions-item label="积分金额">{{ currentOrder.pointsPrice || "0" }}</el-descriptions-item>
<el-descriptions-item label="退款积分">{{ currentOrder.refund || "0" }}</el-descriptions-item>
<el-descriptions-item label="物流单号">{{ currentOrder.logisticsOrderId || "-" }}</el-descriptions-item>
<el-descriptions-item label="承运商">{{ currentOrder.crrgBsnNm || "-" }}</el-descriptions-item>
</el-descriptions>
<span slot="footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
startDate: null,
endDate: null,
reconciliationStatus: null,
invoiceStatus: null
},
reconcileDialogVisible: false,
reconcilePointsTotal: 0,
pointsTotalLoading: false,
executeLoading: false,
reconcileForm: {
supplierId: null,
reconciliationType: 1,
orderId: null,
startDate: null,
endDate: null
},
recordDialogVisible: false,
recordLoading: false,
recordList: [],
recordForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
batchNo: null,
diffType: null,
noticeStatus: null
},
detailDialogVisible: false,
currentOrder: null,
reconciliationStatusOptions: [
{ value: 0, label: "未对账" },
{ value: 1, label: "已对账" },
{ value: 2, label: "对账异常" }
],
invoiceStatusOptions: [
{ value: 0, label: "未开票" },
{ value: 1, label: "已开票" },
{ value: 2, label: "开票中" }
],
orderStateOptions: [
{ value: 0, label: "待付款" },
{ value: 1, label: "已支付" },
{ value: 2, label: "进行中" },
{ value: 3, label: "已取消" },
{ value: 4, label: "已签收" },
{ value: 5, label: "已完成" },
{ value: 6, label: "换货" },
{ value: 7, label: "退货" },
{ value: 8, label: "已废除" }
]
}
},
methods: {
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
recordIndexMethod(index) {
return index + 1 + (this.recordForm.pageNumber - 1) * this.recordForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openReconcileDialog() {
this.reconcileForm = {
supplierId: this.pageForm.supplierId,
reconciliationType: this.pageForm.reconciliationStatus === 2 ? 2 : 1,
orderId: this.pageForm.orderId,
startDate: this.pageForm.startDate,
endDate: this.pageForm.endDate
}
this.reconcilePointsTotal = 0
this.reconcileDialogVisible = true
this.loadPointsTotal()
},
async loadPointsTotal() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) {
this.notifyWarning("请先选择供应商、对账类型和对账日期")
return
}
this.pointsTotalLoading = true
const resp = await this.$axios.post(loc() + "/pointsTotal", this.reconcileForm)
this.pointsTotalLoading = false
if (resp.code === 0) {
this.reconcilePointsTotal = resp.data || 0
} else {
this.notifyWarning(resp.msg)
}
},
async executeReconciliation() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) {
this.notifyWarning("请先选择供应商、对账类型和对账日期")
return
}
this.executeLoading = true
const resp = await this.$axios.post(loc() + "/execute", this.reconcileForm)
this.executeLoading = false
if (resp.code === 0) {
this.notifySuccess(resp.data || "对账完成")
this.reconcileDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
},
openRecordDialog() {
this.recordDialogVisible = true
this.recordForm.supplierId = this.pageForm.supplierId
this.recordForm.orderId = this.pageForm.orderId
this.recordPageData()
},
recordSearch() {
this.recordForm.pageNumber = 1
this.recordPageData()
},
recordReset() {
this.recordForm = {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
batchNo: null,
diffType: null,
noticeStatus: null
}
this.recordPageData()
},
async recordPageData() {
this.recordLoading = true
const resp = await this.$axios.post(loc() + "/recordPage", this.recordForm)
this.recordLoading = false
if (resp.code === 0) {
this.recordList = resp.data.list || []
this.recordForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
recordPageChange(val) {
this.recordForm.pageNumber = val
this.recordPageData()
},
recordSizeChange(val) {
this.recordForm.pageSize = val
this.recordForm.pageNumber = 1
this.recordPageData()
},
showOrderDetail(row) {
this.currentOrder = row
this.detailDialogVisible = true
},
reconciliationStatusName(value) {
const item = this.reconciliationStatusOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
reconciliationStatusType(value) {
const map = { 0: "info", 1: "success", 2: "danger" }
return map[value] || "info"
},
invoiceStatusName(value) {
const item = this.invoiceStatusOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
invoiceStatusType(value) {
const map = { 0: "info", 1: "success", 2: "warning" }
return map[value] || "info"
},
orderStateName(value) {
const item = this.orderStateOptions.find((v) => String(v.value) === String(value))
return item ? item.label : "-"
},
orderStatusType(value) {
const map = {
0: "info",
1: "primary",
2: "warning",
3: "danger",
4: "primary",
5: "success",
6: "warning",
7: "danger",
8: "info"
}
return map[value] || "info"
},
diffTypeName(value) {
return Number(value) === 0 ? "一致" : "异常"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->