Files
lilishop-ui/manager/src/views/statistics/sales-order-detail.vue
pikachu1995@126.com ba964ff478 refactor(manager): 重构统计报表、促销活动与工单审核等12个列表页为 useTable
- 重构统计报表(销售订单明细、商品销售汇总、店铺业绩)列表页为 useTable

- 重构营销活动(秒杀、第N件优惠、积分商品)列表页为 useTable

- 重构财务流水(钱包日志)、服务工单、分销导入、店铺审核、商品参数、订单投诉列表页

- 消除约 210+ 行分页/防抖/竞态重复样板代码,统一加载与查询状态规范
2026-10-20 14:20:36 +08:00

148 lines
5.5 KiB
Vue

<template>
<div class="search">
<el-card>
<el-form inline>
<el-form-item label="发生日期">
<el-date-picker
v-model="dateRange"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
:default-time="defaultTime"
start-placeholder="开始"
end-placeholder="结束"
/>
</el-form-item>
<el-form-item label="订单号">
<el-input v-model="params.orderSn" clearable placeholder="订单编号" style="width: 160px" />
</el-form-item>
<el-form-item label="买家">
<el-input v-model="params.memberName" clearable placeholder="买家名称" style="width: 140px" />
</el-form-item>
<el-form-item label="商品关键词">
<el-input v-model="params.keyword" clearable placeholder="商品名称" style="width: 160px" />
</el-form-item>
<el-form-item label="店铺">
<el-select v-model="params.storeId" clearable filterable placeholder="全部店铺" style="width: 180px">
<el-option v-for="item in shops" :key="item.id" :label="item.storeName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button @click="handleExport">导出</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="mt_10">
<el-table v-loading="loading" border :data="data" style="width: 100%">
<el-table-column label="序号" width="60" align="center">
<template #default="{ $index }">
{{ (params.pageNumber - 1) * params.pageSize + $index + 1 }}
</template>
</el-table-column>
<el-table-column prop="orderRefundSn" label="订单编号|售后单号" min-width="200" show-overflow-tooltip />
<el-table-column prop="occurTime" label="发生时间" width="160" />
<el-table-column prop="flowTypeName" label="交易类型" width="90" />
<el-table-column prop="num" label="成交数量" width="90" align="right" />
<el-table-column label="成交金额" width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.transactionAmount) }}</template>
</el-table-column>
<el-table-column label="售价金额" width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.salePriceAmount) }}</template>
</el-table-column>
<el-table-column label="优惠金额" width="110" align="right">
<template #default="{ row }">{{ formatMoney(row.discountAmount) }}</template>
</el-table-column>
<el-table-column prop="paymentDetail" label="收款明细" width="120" show-overflow-tooltip />
<el-table-column prop="discountDetail" label="优惠明细" min-width="160" show-overflow-tooltip />
<el-table-column label="订单来源" width="100">
<template #default="{ row }">{{ clientTypeText(row.clientType) }}</template>
</el-table-column>
</el-table>
<el-pagination
v-model:current-page="params.pageNumber"
v-model:page-size="params.pageSize"
class="mt_10"
:total="total"
:page-sizes="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@current-change="loadData"
@size-change="loadData"
/>
</el-card>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { useTable } from "@/utils/useTable";
import * as API_Statistics from "@/api/statistics";
import { getShopListData } from "@/api/shops";
import { downloadBlob } from "@/utils/downloadBlob";
import { unitPrice } from "@/utils/filters";
defineOptions({ name: "sales-order-detail-report" });
const dateRange = ref([]);
const defaultTime = ref([new Date(2000, 0, 1, 0, 0, 0), new Date(2000, 0, 1, 23, 59, 59)]);
const { loading, data, total, searchForm: params, getDataList: loadData, handleSearch } = useTable(
API_Statistics.getSalesOrderDetailReport,
{
pageSize: 20,
immediate: false,
form: { storeId: "", orderSn: "", memberName: "", keyword: "" },
beforeRequest: buildParams,
}
);
const shops = ref([]);
onMounted(() => {
initDefaultRange();
loadShops();
loadData();
});
function initDefaultRange() {
const end = new Date();
const start = new Date();
start.setDate(start.getDate() - 6);
dateRange.value = [formatDate(start, true), formatDate(end, false)];
}
function formatDate(date, startOfDay) {
const y = date.getFullYear();
const m = String(date.getMonth() + 1).padStart(2, "0");
const d = String(date.getDate()).padStart(2, "0");
return startOfDay ? `${y}-${m}-${d} 00:00:00` : `${y}-${m}-${d} 23:59:59`;
}
function loadShops() {
getShopListData({ pageNumber: 1, pageSize: 200 }).then((res) => {
if (res.success) shops.value = res.result.records || [];
});
}
function buildParams(base = params) {
const p = { ...base };
if (dateRange.value && dateRange.value.length === 2) {
p.startTime = dateRange.value[0];
p.endTime = dateRange.value[1];
}
return p;
}
function handleExport() {
API_Statistics.exportSalesOrderDetailReport(buildParams()).then((blob) => {
downloadBlob(blob, "销售订单明细报表.xlsx");
});
}
function formatMoney(val) {
return unitPrice(val, "¥");
}
function clientTypeText(val) {
const map = {
H5: "移动端",
PC: "PC端",
WECHAT_MP: "小程序",
APP: "移动应用端",
UNKNOWN: "未知",
};
return map[val] || val || "";
}
</script>