feat: 积分商城-对账-发票-优化

This commit is contained in:
2026-09-03 18:19:03 +08:00
parent 72dfff50a6
commit 9159f8912d
14 changed files with 558 additions and 234 deletions
@@ -86,7 +86,7 @@ layout("/layouts/platform.html"){
<el-input clearable placeholder="请输入发票抬头" v-model="applyForm.invoiceTitle"></el-input>
</el-form-item>
<el-form-item label="纳税人识别号" required>
<el-input clearable placeholder="请输入纳税人识别号" v-model="applyForm.invoiceCode"></el-input>
<el-input clearable placeholder="请输入纳税人识别号" v-model="applyForm.taxIdNumber"></el-input>
</el-form-item>
<el-form-item label="发票地址" required>
<el-input clearable placeholder="请输入发票地址" v-model="applyForm.invoiceAddress"></el-input>
@@ -275,7 +275,7 @@ layout("/layouts/platform.html"){
endDate: null,
invoiceDate: null,
invoiceTitle: null,
invoiceCode: null,
taxIdNumber: null,
invoiceAddress: null,
invoiceContact: null,
invoiceType: 10,
@@ -3,44 +3,74 @@ 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 :body-style="{ paddingBottom: '2px' }" shadow="never">
<el-form label-width="96px" @submit.native.prevent>
<el-row :gutter="8">
<el-col :lg="4" :md="12" :xs="24">
<el-form-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>
</el-form-item>
</el-col>
<el-col :lg="4" :md="12" :xs="24">
<el-form-item label="起始日期">
<el-date-picker clearable placeholder="开始日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.startDate"></el-date-picker>
</el-form-item>
</el-col>
<el-col :lg="4" :md="12" :xs="24">
<el-form-item label="结束日期">
<el-date-picker clearable placeholder="结束日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="pageForm.endDate"></el-date-picker>
</el-form-item>
</el-col>
<el-col :lg="4" :md="12" :xs="24">
<el-form-item label="对账类型">
<el-select clearable placeholder="请选择对账类型" style="width: 100%" v-model="pageForm.reconciliationType">
<el-option label="正常订单对账" :value="1"></el-option>
<el-option label="历史异常订单对账" :value="2"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :lg="4" :md="12" :xs="24">
<el-form-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>
</el-form-item>
</el-col>
<el-col :lg="4" :md="12" :xs="24">
<el-form-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>
</el-form-item>
</el-col>
<el-col :lg="6" :md="24" :xs="24">
<el-form-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="pageForm.orderId"></el-input>
</el-form-item>
</el-col>
<el-col :lg="12" :md="24" :xs="24">
<el-form-item label-width="16px">
<el-button @click="doSearch" native-type="button" type="primary">查询</el-button>
<el-button @click="resetSearch" native-type="button">重置</el-button>
<el-button @click="openReconcileDialog" native-type="button" type="success">对账</el-button>
<el-button
v-if="pageForm.reconciliationStatus === 1 && hasSearched && pageForm.invoiceStatus === 0"
@click="openApplyDialog"
native-type="button"
type="warning"
>开票申请</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</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">
<table-tool label="对账订单列表"></table-tool>
<el-table ref="tableRef" :data="tableData" @selection-change="selectionChange" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column align="center" type="selection" width="50"></el-table-column>
<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>
@@ -87,39 +117,43 @@ layout("/layouts/platform.html"){
</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>
<el-descriptions :column="1" border size="small">
<el-descriptions-item label="本次对账金额">{{ reconcilePointsTotal }}</el-descriptions-item>
</el-descriptions>
<el-alert :closable="false" show-icon style="margin-top: 16px" 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="applyDialogVisible" top="5vh" width="720px">
<el-form ref="applyFormRef" :model="applyForm" :rules="applyRules" label-width="120px" size="small">
<el-form-item label="开票日期" prop="invoiceDate">
<el-date-picker placeholder="请选择开票日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="applyForm.invoiceDate"></el-date-picker>
</el-form-item>
<el-form-item label="发票抬头" prop="invoiceTitle" required>
<el-input clearable placeholder="请输入发票抬头" v-model="applyForm.invoiceTitle"></el-input>
</el-form-item>
<el-form-item label="纳税人识别号" prop="taxIdNumber" required>
<el-input clearable placeholder="请输入纳税人识别号" v-model="applyForm.taxIdNumber"></el-input>
</el-form-item>
<el-form-item label="开户地址" prop="invoiceAddress" required>
<el-input clearable placeholder="请输入发票地址" v-model="applyForm.invoiceAddress"></el-input>
</el-form-item>
<el-form-item label="联系电话" prop="invoiceContact" required>
<el-input clearable maxlength="11" placeholder="请输入联系电话" v-model="applyForm.invoiceContact"></el-input>
</el-form-item>
<el-form-item label="发票类型" prop="invoiceType" required>
<dict-select clearable code="invoice_type" placeholder="请选择发票类型" style="width: 100%" v-model="applyForm.invoiceType"></dict-select>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="applyDialogVisible = false">取消</el-button>
<el-button @click="submitApply" :loading="applyLoading" 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="供应商">
@@ -209,6 +243,8 @@ layout("/layouts/platform.html"){
supplierList: [],
tableData: [],
tableLoading: false,
selections: [],
hasSearched: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
@@ -218,6 +254,7 @@ layout("/layouts/platform.html"){
userId: null,
startDate: null,
endDate: null,
reconciliationType: null,
reconciliationStatus: null,
invoiceStatus: null
},
@@ -232,6 +269,36 @@ layout("/layouts/platform.html"){
startDate: null,
endDate: null
},
applyDialogVisible: false,
applyLoading: false,
applyForm: {
invoiceDate: null,
invoiceTitle: null,
taxIdNumber: null,
invoiceAddress: null,
invoiceContact: null,
invoiceType: null
},
applyRules: {
invoiceDate: [{ required: true, message: "请选择开票日期", trigger: "change" }],
invoiceTitle: [{ required: true, message: "请输入发票抬头", trigger: "blur" }],
taxIdNumber: [{ required: true, message: "请输入纳税人识别号", trigger: "blur" }],
invoiceAddress: [{ required: true, message: "请输入开户地址", trigger: "blur" }],
invoiceContact: [
{ required: true, message: "请输入联系电话", trigger: "blur" },
{
validator(rule, value, callback) {
if (value && !/^1[3-9]\d{9}$/.test(value)) {
callback(new Error("请输入正确的手机号码"))
} else {
callback()
}
},
trigger: "blur"
}
],
invoiceType: [{ required: true, message: "请选择发票类型", trigger: "change" }]
},
recordDialogVisible: false,
recordLoading: false,
recordList: [],
@@ -279,9 +346,32 @@ layout("/layouts/platform.html"){
return index + 1 + (this.recordForm.pageNumber - 1) * this.recordForm.pageSize
},
doSearch() {
this.hasSearched = true
this.selections = []
this.pageForm.pageNumber = 1
this.pageData()
},
resetSearch() {
this.pageForm = {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
userId: null,
startDate: null,
endDate: null,
reconciliationType: null,
reconciliationStatus: null,
invoiceStatus: null
}
this.hasSearched = false
this.selections = []
this.pageData()
},
selectionChange(rows) {
this.selections = rows || []
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
@@ -308,48 +398,114 @@ layout("/layouts/platform.html"){
this.supplierList = resp.data || []
}
},
openReconcileDialog() {
async openReconcileDialog() {
this.reconcileForm = {
supplierId: this.pageForm.supplierId,
reconciliationType: this.pageForm.reconciliationStatus === 2 ? 2 : 1,
reconciliationType: this.pageForm.reconciliationType,
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("请先选择供应商、对账类型和对账日期")
if (!this.reconciliationParamsComplete()) {
this.notifyWarning(this.reconcileForm.reconciliationType === 2 ? "请选择供应商、对账类型、对账日期并填写订单ID" : "请选择供应商、对账类型和对账日期")
return
}
if (await this.loadPointsTotal()) {
this.reconcileDialogVisible = true
}
},
reconciliationParamsComplete() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.reconciliationType) {
return false
}
if (!this.reconcileForm.startDate || !this.reconcileForm.endDate) {
return false
}
return this.reconcileForm.reconciliationType !== 2 || !!this.reconcileForm.orderId
},
async loadPointsTotal() {
if (!this.reconciliationParamsComplete()) {
return false
}
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 {
try {
const resp = await this.$axios.post(loc() + "/pointsTotal", this.reconcileForm)
if (resp.code === 0) {
this.reconcilePointsTotal = resp.data || 0
return true
}
this.notifyWarning(resp.msg)
return false
} finally {
this.pointsTotalLoading = false
}
},
async executeReconciliation() {
if (!this.reconcileForm.supplierId || !this.reconcileForm.startDate || !this.reconcileForm.endDate || !this.reconcileForm.reconciliationType) {
this.notifyWarning("请先选择供应商、对账类型和对账日期")
if (!this.reconciliationParamsComplete()) {
this.notifyWarning(this.reconcileForm.reconciliationType === 2 ? "请先选择供应商并填写订单ID" : "请先选择供应商、对账类型和对账日期")
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)
try {
const resp = await this.$axios.post(loc() + "/execute", this.reconcileForm)
if (resp.code === 0) {
this.notifySuccess(resp.data || "对账完成")
this.reconcileDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
} finally {
this.executeLoading = false
}
},
openApplyDialog() {
if (!this.pageForm.supplierId || !this.pageForm.startDate || !this.pageForm.endDate) {
return
}
if (!this.selections.length) {
this.notifyWarning("请先勾选需要开票的订单")
return
}
this.applyForm = {
invoiceDate: null,
invoiceTitle: null,
taxIdNumber: null,
invoiceAddress: null,
invoiceContact: null,
invoiceType: null
}
this.applyDialogVisible = true
},
submitApply() {
this.$refs.applyFormRef.validate(async (valid) => {
if (!valid) {
return
}
this.applyLoading = true
try {
const params = Object.assign({}, this.applyForm, {
supplierId: this.pageForm.supplierId,
startDate: this.pageForm.startDate,
endDate: this.pageForm.endDate,
orderIds: this.selections.map((item) => item.orderId).join(",")
})
const resp = await this.$axios.post("/platform/zhgh/points-mall/invoice/applyInvoice", params)
if (resp.code === 0) {
this.notifySuccess(resp.data || "开票申请成功")
this.applyDialogVisible = false
this.selections = []
this.$refs.tableRef.clearSelection()
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
} finally {
this.applyLoading = false
}
})
},
openRecordDialog() {
this.recordDialogVisible = true
this.recordForm.supplierId = this.pageForm.supplierId
@@ -146,36 +146,12 @@ layout("/layouts/platform_h5.html"){
this.userPoints = resp.data || 0
}
},
async openSupplier(item) {
openSupplier(item) {
if (!item.supplierUrl) {
this.$toast("暂无跳转地址")
return
}
const virtualId = this.$store && this.$store.state.user ? this.$store.state.user.virtualId : ""
if (!virtualId) {
this.$toast("获取用户信息失败,请重新登录后再试")
return
}
window.location.href = this.withSupplierQuery(item.supplierUrl, {
userId: virtualId
})
},
withSupplierQuery(url, params) {
const query = Object.keys(params)
.filter((key) => params[key] !== undefined && params[key] !== null && params[key] !== "")
.map((key) => encodeURIComponent(key) + "=" + encodeURIComponent(params[key]))
.join("&")
if (!query) {
return url
}
const hashIndex = url.indexOf("#")
if (hashIndex > -1) {
const base = url.slice(0, hashIndex)
const hash = url.slice(hashIndex)
const joiner = hash.indexOf("?") > -1 ? "&" : "?"
return base + hash + joiner + query
}
return url + (url.indexOf("?") > -1 ? "&" : "?") + query
window.location.href = item.supplierUrl
},
onImgError(e) {
e.target.src = this.defaultLogo