✨ 客户账单弹窗增加汇总和导出

This commit is contained in:
caojia
2026-10-27 17:21:12 +08:00
parent b4e696b27c
commit db787ec7f7
8 changed files with 166 additions and 16 deletions
@@ -1,7 +1,9 @@
package com.bms.web.controller.customer.customermoney;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
@@ -13,7 +15,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.bms.ar.domain.ArBill;
import com.bms.ar.domain.ArBalanceWriteoff;
import com.bms.ar.domain.vo.ArBillListExportRow;
import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.ar.domain.vo.ArBillSummary;
import com.bms.ar.service.IArBalanceWriteoffService;
import com.bms.ar.service.IArBillService;
import com.bms.common.annotation.Log;
@@ -21,6 +25,7 @@ import com.bms.common.core.controller.BaseController;
import com.bms.common.core.domain.AjaxResult;
import com.bms.common.core.page.TableDataInfo;
import com.bms.common.enums.BusinessType;
import com.bms.common.utils.poi.ExcelUtil;
/**
* 客户余额账单查询与核销 Controller
@@ -43,17 +48,50 @@ public class CustomerBillController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:list')")
@GetMapping("/list/{customerId}")
public TableDataInfo billList(@PathVariable("customerId") Long customerId, ArBillQuery query)
public AjaxResult billList(@PathVariable("customerId") Long customerId, ArBillQuery query)
{
if (customerId == null || customerId <= 0L)
{
return getDataTable(Collections.emptyList());
AjaxResult result = success();
result.put("rows", Collections.emptyList());
result.put("total", 0L);
result.put("summary", new ArBillSummary());
return result;
}
ArBillQuery billQuery = query == null ? new ArBillQuery() : query;
billQuery.setCustomerId(customerId);
startPage();
List<ArBill> list = billService.selectArBillList(billQuery);
return getDataTable(list);
TableDataInfo pageData = getDataTable(list);
AjaxResult result = success();
result.put("rows", pageData.getRows());
result.put("total", pageData.getTotal());
result.put("summary", billService.selectArBillSummary(billQuery));
return result;
}
/**
* 按当前筛选条件导出指定客户的全部应收账单,不受列表分页参数影响。
*/
@PreAuthorize("@ss.hasPermi('customersupplier:customerinfo:export')")
@Log(title = "客户账单", businessType = BusinessType.EXPORT)
@PostMapping("/export/{customerId}")
public void export(HttpServletResponse response, @PathVariable("customerId") Long customerId, ArBillQuery query)
{
List<ArBillListExportRow> rows = new ArrayList<>();
if (customerId != null && customerId > 0L)
{
ArBillQuery billQuery = query == null ? new ArBillQuery() : query;
// 客户ID固定取自路径,忽略请求参数中可能出现的 customerId。
billQuery.setCustomerId(customerId);
List<ArBill> bills = billService.selectArBillList(billQuery);
for (ArBill bill : bills)
{
rows.add(ArBillListExportRow.from(bill));
}
}
ExcelUtil<ArBillListExportRow> util = new ExcelUtil<>(ArBillListExportRow.class);
util.exportExcel(response, rows, "客户账单");
}
/**
@@ -0,0 +1,16 @@
package com.bms.ar.domain.vo;
import java.io.Serializable;
import java.math.BigDecimal;
import lombok.Data;
/** 应收账单列表按筛选条件汇总的金额。 */
@Data
public class ArBillSummary implements Serializable {
private static final long serialVersionUID = 1L;
private BigDecimal totalAmount = BigDecimal.ZERO;
private BigDecimal writtenOffAmount = BigDecimal.ZERO;
private BigDecimal unwrittenAmount = BigDecimal.ZERO;
}
@@ -7,10 +7,13 @@ import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.bms.ar.domain.ArBill;
import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.ar.domain.vo.ArBillSummary;
public interface ArBillMapper {
List<ArBill> selectArBillList(ArBillQuery query);
ArBillSummary selectArBillSummary(ArBillQuery query);
ArBill selectArBillById(Long arBillId);
ArBill selectArBillByBillNo(String billNo);
@@ -14,6 +14,7 @@ import com.bms.ar.domain.vo.ArBillGenerateRequest;
import com.bms.ar.domain.vo.ArBillGenerateQuery;
import com.bms.ar.domain.vo.ArBillActionRequest;
import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.ar.domain.vo.ArBillSummary;
import com.bms.price.domain.PrcOrderBill;
/**
@@ -25,6 +26,8 @@ public interface IArBillService
{
List<ArBill> selectArBillList(ArBillQuery query);
ArBillSummary selectArBillSummary(ArBillQuery query);
ArBill selectArBillById(Long arBillId);
List<ArBillDetail> selectArBillDetails(Long arBillId);
@@ -79,6 +79,7 @@ import com.bms.ar.domain.vo.ArBillActionRequest;
import com.bms.ar.domain.vo.ArBillGenerateQuery;
import com.bms.ar.domain.vo.ArBillGenerateRequest;
import com.bms.ar.domain.vo.ArBillQuery;
import com.bms.ar.domain.vo.ArBillSummary;
import com.bms.common.config.BmsConfig;
import com.bms.common.exception.ServiceException;
import com.bms.common.utils.DateUtils;
@@ -223,6 +224,14 @@ public class ArBillServiceImpl implements IArBillService {
return bills;
}
/**
* 汇总当前筛选条件下的全部账单金额,不受列表分页影响。
*/
@Override
public ArBillSummary selectArBillSummary(ArBillQuery query) {
return billMapper.selectArBillSummary(query);
}
/**
* 查询账单头信息;导出记录随账单头返回,明细由独立分页接口查询。
*/
@@ -35,6 +35,12 @@
<result property="updateTime" column="update_time"/>
</resultMap>
<resultMap id="ArBillSummaryResult" type="com.bms.ar.domain.vo.ArBillSummary">
<result property="totalAmount" column="total_amount"/>
<result property="writtenOffAmount" column="written_off_amount"/>
<result property="unwrittenAmount" column="unwritten_amount"/>
</resultMap>
<sql id="selectArBillColumns">
select ar_bill_id,
bill_no,
@@ -67,8 +73,7 @@
from ar_bill
</sql>
<select id="selectArBillList" parameterType="com.bms.ar.domain.vo.ArBillQuery" resultMap="ArBillResult">
<include refid="selectArBillColumns"/>
<sql id="arBillListFilter">
<where>
and (generation_status is null or generation_status = 'READY')
<if test="billNo != null and billNo != ''">and bill_no like concat('%', #{billNo}, '%')</if>
@@ -86,9 +91,22 @@
<if test="billDateEnd != null">and bill_date &lt;= #{billDateEnd}</if>
<if test="billPeriodEnd != null">and bill_period_end = #{billPeriodEnd}</if>
</where>
</sql>
<select id="selectArBillList" parameterType="com.bms.ar.domain.vo.ArBillQuery" resultMap="ArBillResult">
<include refid="selectArBillColumns"/>
<include refid="arBillListFilter"/>
order by create_time desc, ar_bill_id desc
</select>
<select id="selectArBillSummary" parameterType="com.bms.ar.domain.vo.ArBillQuery" resultMap="ArBillSummaryResult">
select coalesce(sum(total_amount), 0) as total_amount,
coalesce(sum(written_off_amount), 0) as written_off_amount,
coalesce(sum(unwritten_amount), 0) as unwritten_amount
from ar_bill
<include refid="arBillListFilter"/>
</select>
<select id="selectArBillById" parameterType="long" resultMap="ArBillResult">
<include refid="selectArBillColumns"/>
where ar_bill_id = #{arBillId}
+22 -9
View File
@@ -126,15 +126,18 @@
</template>
</el-table>
<!-- 分页 -->
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:page-sizes="pageSizes"
@pagination="getList"
/>
<!-- 分页;可选的分页前置内容用于展示汇总等信息。 -->
<div class="basic-table-pagination" :class="{ 'basic-table-pagination--with-extra': $slots['pagination-extra'] }">
<slot name="pagination-extra" />
<pagination
v-show="total > 0"
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
:page-sizes="pageSizes"
@pagination="getList"
/>
</div>
</div>
</template>
@@ -727,6 +730,16 @@ onBeforeUnmount(() => {
.basic-table {
width: 100%;
}
.basic-table-pagination--with-extra {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.basic-table-pagination--with-extra :deep(.pagination-container) {
margin-left: auto;
}
.basic-table-virtual {
width: 100%;
min-height: 240px;
@@ -16,8 +16,24 @@
:border="true"
:max-height="560"
:page-size="10"
ref="tableRef"
row-key="arBillId"
>
<template #search-extra>
<el-button
type="warning"
icon="Download"
@click="handleExport"
v-hasPermi="['customersupplier:customerinfo:export']"
>导出</el-button>
</template>
<template #pagination-extra>
<div class="bill-summary">
<span>应收总额:<b class="amount">{{ money(billSummary.totalAmount) }}</b></span>
<span>已核销:<b class="written">{{ money(billSummary.writtenOffAmount) }}</b></span>
<span>未核销:<b class="unwritten">{{ money(billSummary.unwrittenAmount) }}</b></span>
</div>
</template>
<template #col-status="{ row }">
<el-tag :type="statusType(row.status)">{{ statusText(row.status) }}</el-tag>
</template>
@@ -38,7 +54,7 @@
</template>
<script setup>
import { computed } from 'vue'
import { computed, getCurrentInstance, ref } from 'vue'
import { listCustomerBills } from '@/api/customersuppliermanagement/customermoney/customerbill'
import { AR_BILL_STATUS_OPTIONS, AR_BILL_STATUS_TYPES } from '@/constants/ar/bill'
import { arEnumLabel } from '@/constants/ar/enums'
@@ -48,6 +64,7 @@ const props = defineProps({
customerId: { type: [String, Number], required: true },
customerName: { type: String, default: '' }
})
const { proxy } = getCurrentInstance()
const emit = defineEmits(['update:modelValue'])
const visible = computed({
@@ -58,6 +75,9 @@ const dialogTitle = computed(() => props.customerName ? `客户账单 - ${props.
// BasicTable merges these fixed parameters after search and pagination state.
const fixedParams = computed(() => ({ customerId: Number(props.customerId) }))
const tableRef = ref()
const billSummary = ref({ totalAmount: 0, writtenOffAmount: 0, unwrittenAmount: 0 })
let querySerial = 0
const statusOptions = AR_BILL_STATUS_OPTIONS
const columns = [
{ label: '账单号', prop: 'billNo', minWidth: 180, showOverflowTooltip: true },
@@ -81,6 +101,28 @@ const searchColumns = [
]
function queryBills(params) {
const query = normalizeBillQuery(params)
const serial = ++querySerial
return listCustomerBills(props.customerId, query).then(response => {
if (serial === querySerial) {
billSummary.value = response.summary || { totalAmount: 0, writtenOffAmount: 0, unwrittenAmount: 0 }
}
return response
})
}
function handleExport() {
const query = normalizeBillQuery(tableRef.value?.getQueryParams() || {})
delete query.pageNum
delete query.pageSize
proxy.download(
`/customersupplier/customerbill/export/${props.customerId}`,
query,
`客户账单_${props.customerId}_${Date.now()}.xlsx`
)
}
function normalizeBillQuery(params) {
const query = { ...params }
const range = query.billDateRange
if (Array.isArray(range) && range.length === 2) {
@@ -90,7 +132,7 @@ function queryBills(params) {
delete query.billDateRange
// The route parameter is authoritative; the customer ID cannot be changed by search/reset.
delete query.customerId
return listCustomerBills(props.customerId, query)
return query
}
function date(value) { return value ? String(value).slice(0, 10) : '-' }
@@ -103,6 +145,14 @@ function statusType(value) { return AR_BILL_STATUS_TYPES[value] || 'info' }
</script>
<style scoped>
.bill-summary {
display: flex;
flex-wrap: wrap;
gap: 8px 20px;
color: var(--el-text-color-regular);
font-size: 13px;
}
.bill-summary b { font-weight: 600; }
.amount { color: #f56c6c; font-weight: 600; }
.written { color: var(--el-color-success); }
.unwritten { color: var(--el-color-warning); }