Files
cashier-web/src/views/data/sales.vue
2026-01-05 13:36:24 +08:00

519 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<!-- <el-tabs v-model="orderType" @tab-click="getTableData">
<el-tab-pane label="收款" name="1"></el-tab-pane>
<el-tab-pane label="销量" name="2"></el-tab-pane>
</el-tabs> -->
<div class="head-container">
<el-form :model="query" inline label-position="left">
<template v-if="orderType == 2">
<el-form-item>
<el-input placeholder="商品名称" v-model="query.productName" clearable />
</el-form-item>
<el-form-item v-if="isHeadShop == 1 && loginType == 0">
<el-select v-model="shopId" placeholder="选择分店" style="width: 200px; margin-right: 10px"
@change="getCategory">
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
</el-select>
</el-form-item>
<el-form-item>
<el-select v-model="query.prodCategoryId" placeholder="商品分类" style="width: 140px">
<el-option :label="item.name" :value="item.id" v-for="item in categorys" :key="item.id"></el-option>
</el-select>
</el-form-item>
</template>
<el-form-item>
<el-radio-group v-model="timeValue" @change="timeChange">
<!-- <el-radio-button value="">全部</el-radio-button> -->
<el-radio-button value="today">今天</el-radio-button>
<el-radio-button value="yesterday">昨天</el-radio-button>
<el-radio-button value="last_7_days">最近7天</el-radio-button>
<el-radio-button value="last_30_days">最近30天</el-radio-button>
<el-radio-button value="this_week">本周</el-radio-button>
<el-radio-button value="this_month">本月</el-radio-button>
<el-radio-button value="custom">自定义</el-radio-button>
</el-radio-group>
<el-date-picker class="u-m-l-10" v-model="query.createdAt" type="daterange" range-separator="至"
start-placeholder="开始日期" end-placeholder="结束日期" value-format="YYYY-MM-DD" :disabled-date="disabledDate"
v-if="timeValue == 'custom'"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="tableData.loading" @click="getTableData">查询</el-button>
<el-button icon="Refresh" :loading="tableData.loading" @click="resetHandle">重置</el-button>
<el-button icon="download" v-loading="downloadLoading" @click="downloadHandle">
<span v-if="!downloadLoading">导出Excel</span>
<span v-else>下载中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<div class="head-container">
<div class="collect_wrap">
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<Coin />
</el-icon>
</div>
<div class="info">
<div class="m">{{ multiplyAndFormat(payCount.totalAmount || 0) }}</div>
<div class="t">总金额</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<Coin />
</el-icon>
</div>
<div class="info">
<div class="m">{{ multiplyAndFormat(payCount.refundAmount || 0) }}</div>
<div class="t">退款金额</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<ShoppingCartFull />
</el-icon>
</div>
<div class="info">
<div class="m">{{ payCount.saleCount || 0 }}</div>
<div class="t">总数量</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<ShoppingCart />
</el-icon>
</div>
<div class="info">
<div class="m">{{ payCount.refundCount || 0 }}</div>
<div class="t">退单量</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<ShoppingCart />
</el-icon>
</div>
<div class="info">
<div class="m">{{ multiplyAndFormat((payCount.totalAmount - payCount.refundAmount) || 0) }}</div>
<div class="t">实际销售额总和</div>
</div>
</div>
<div class="item">
<div class="icon_wrap" style="--bg-color: #c978ee">
<el-icon color="#fff">
<ShoppingCart />
</el-icon>
</div>
<div class="info">
<div class="m">{{ multiplyAndFormat((payCount.saleCount - payCount.refundCount) || 0) }}</div>
<div class="t">实际销量总和</div>
</div>
</div>
</div>
</div>
<div class="head-container">
<el-table :data="tableData.data" v-loading="tableData.loading" v-if="orderType == 1">
<el-table-column label="日期" prop="tradeDay"></el-table-column>
<el-table-column label="营业额" prop="total">
<template v-slot="scope">{{ scope.row.total }}</template>
</el-table-column>
<el-table-column label="销售额" prop="total">
<template v-slot="scope">
{{ scope.row.total }}|{{ totalfilter(scope.row.refund) }}
</template>
</el-table-column>
<el-table-column label="微信小程序支付" prop="wxLite">
<template v-slot="scope">{{ scope.row.wxLite }}</template>
</el-table-column>
<el-table-column label="扫码支付金额" prop="scanCode">
<template v-slot="scope">{{ scope.row.scanCode }}</template>
</el-table-column>
<el-table-column label="现金支付金额" prop="cash">
<template v-slot="scope">{{ scope.row.cash }}</template>
</el-table-column>
<el-table-column label="会员充值" prop="cash">
<template v-slot="scope">{{ scope.row.recharge }}</template>
</el-table-column>
<el-table-column label="会员支付" prop="cash">
<template v-slot="scope">{{ scope.row.deposit }}</template>
</el-table-column>
<!-- <el-table-column label="充值金额" prop="cash">
<template v-slot="scope">
{{ scope.row.recharge }}
</template>
</el-table-column> -->
<el-table-column label="退款金额" prop="cash">
<template v-slot="scope">{{ scope.row.refund }}</template>
</el-table-column>
<el-table-column label="实际销量" prop="refundCount">
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleCount - scope.row.refundCount) }} </template>
</el-table-column>
<el-table-column label="实际销售额" prop="refundCount">
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleAmount - scope.row.refundAmount) }} </template>
</el-table-column>
</el-table>
<el-table :data="tableData.data" v-loading="tableData.loading" v-if="orderType == 2">
<!-- <el-table-column label="商品分类" prop="cateName"></el-table-column>
<el-table-column label="商品描述" prop="productSkuName"></el-table-column>
<el-table-column label="单价" prop="price"></el-table-column> -->
<el-table-column label="商品名称" prop="productName"></el-table-column>
<el-table-column label="销量" prop="saleCount" sortable></el-table-column>
<el-table-column label="销售金额" prop="saleAmount" sortable>
<template v-slot="scope">{{ multiplyAndFormat(scope.row.saleAmount) }}</template>
</el-table-column>
<el-table-column label="退单量" prop="refundCount" sortable></el-table-column>
<el-table-column label="退款金额" prop="refundAmount" sortable>
<template v-slot="scope">{{ multiplyAndFormat(scope.row.refundAmount) }}</template>
</el-table-column>
<el-table-column label="实际销量" prop="refundCount" sortable>
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleCount - scope.row.refundCount) }} </template>
</el-table-column>
<el-table-column label="实际销售额" prop="refundCount" sortable>
<template v-slot="scope"> {{ multiplyAndFormat(scope.row.saleAmount - scope.row.refundAmount) }} </template>
</el-table-column>
</el-table>
</div>
<div class="head-container">
<el-pagination :total="tableData.total" :current-page="tableData.page" :page-size="tableData.size"
@current-change="paginationChange" @size-change="sizeChange"
layout="total, sizes, prev, pager, next, jumper"></el-pagination>
</div>
</div>
</template>
<script>
import _ from 'lodash'
import saleSummaryApi from "@/api/order/sale-summary";
import categoryApi from "@/api/product/productclassification";
import ShopApi from "@/api/account/shop";
import dayjs from "dayjs";
import { downloadFile, multiplyAndFormat } from "@/utils/index";
import { formatDateRange } from './utils/index.js'
export default {
data() {
return {
multiplyAndFormat,
timeValue: "today",
resetQuery: null,
orderType: "2",
categorys: [],
query: {
createdAt: [dayjs().format("YYYY-MM-DD"), dayjs().format("YYYY-MM-DD")],
productName: "",
prodCategoryId: "",
},
tableData: {
totalList: [],
data: [],
page: 1,
size: 10,
loading: false,
total: 0,
},
downloadLoading: false,
payCount: "",
payCountTotal: 0,
branchList: [],
shopId: null,
isHeadShop: JSON.parse(localStorage.getItem("userInfo")).isHeadShop,
loginType: localStorage.getItem("loginType"),
shopInfo: JSON.parse(localStorage.getItem("userInfo")),
disabledDate: (time) => {
// dayjs().startOf('day'):获取今天的 00:00:00
// dayjs(time):将原生 Date 转为 dayjs 对象
// isAfter判断目标日期是否在今天之后
return dayjs(time).isAfter(dayjs().startOf('day'));
}
};
},
filters: {
timeFilter(time) {
return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
},
},
mounted() {
let shopInfo = JSON.parse(localStorage.getItem('userInfo'))
if (shopInfo.isHeadShop) {
this.shopId = shopInfo.id
} else {
this.shopId = localStorage.getItem('shopId')
}
this.resetQuery = { ...this.query };
this.getTableData();
this.getCategory();
this.geiShopList();
},
methods: {
/**
* 获取分店列表
*/
async geiShopList() {
if (this.shopInfo.isHeadShop) {
let res = await ShopApi.getBranchList()
this.branchList = res;
this.shopId = res[0].shopId
} else {
this.shopId = this.shopInfo.id
}
},
totalfilter(item, d) {
let num = item + d;
return num.toFixed(2);
},
// 获取商品分类
async getCategory() {
try {
this.query.prodCategoryId = ""
const res = await categoryApi.getList({
page: 1,
size: 200,
orderBy: "name asc",
shopId: this.shopId
});
this.categorys = res;
} catch (error) {
console.log(error);
}
},
// 获取订单汇总
async daycount() {
try {
// if (this.query.createdAt[1]) {
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
// }
const res = await saleSummaryApi.count({
beginDate: this.query.createdAt[0],
endDate: this.query.createdAt[1],
rangeType: this.timeValue,
categoryId: this.query.prodCategoryId,
productName: this.query.productName,
shopId: this.shopId,
type: this.orderType,
});
this.payCount = res;
} catch (error) {
console.log(error);
}
},
// 导出Excel
async downloadHandle() {
try {
this.downloadLoading = true;
// if (this.query.createdAt[1]) {
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
// }
const file = await saleSummaryApi.export({
beginDate: this.query.createdAt[0],
endDate: this.query.createdAt[1],
categoryId: this.query.prodCategoryId,
productName: this.query.productName,
shopId: this.shopId,
rangeType: this.timeValue,
});
downloadFile(file, "数据", "xlsx");
} catch (error) {
console.log(error);
}
this.downloadLoading = false;
},
// 重置查询
resetHandle() {
this.timeValue = 'today';
this.query = { ...this.resetQuery };
this.page = 1;
this.getTableData();
},
// 分页大小改变
sizeChange(e) {
this.tableData.size = e;
this.getTableData();
},
// 分页回调
paginationChange(e) {
this.tableData.page = e;
this.tableData.data = this.tableData.totalList[this.tableData.page - 1] || [];
// this.getTableData();
},
async getTableData() {
this.tableData.loading = true;
try {
this.daycount();
// if (this.query.createdAt[1]) {
// this.query.createdAt.splice(1, 1, this.query.createdAt[1].replace("00:00:00", "23:59:59"))
// }
const res = await saleSummaryApi.page({
page: this.tableData.page,
size: this.tableData.size,
type: this.orderType,
beginDate: this.query.createdAt[0],
endDate: this.query.createdAt[1],
rangeType: this.timeValue,
productName: this.query.productName,
categoryId: this.query.prodCategoryId,
shopId: this.shopId,
});
this.tableData.totalList = _.chunk(res, this.tableData.size);
this.tableData.data = this.tableData.totalList[this.tableData.page - 1] || [];
this.tableData.total = res.length;
} catch (error) {
console.log(error);
}
setTimeout(() => {
this.tableData.loading = false;
}, 500);
},
// 切换时间
timeChange(e) {
if (e !== 'custom') {
this.query.createdAt = formatDateRange(e)
} else {
this.query.createdAt = []
}
},
},
};
</script>
<style scoped lang="scss">
.collect_wrap {
display: flex;
gap: 14px;
justify-content: space-between;
.item:nth-child(1) {
background-image: url(../../assets/images/home/data_forms4.png);
}
.item:nth-child(2) {
background-image: url(../../assets/images/home/data_forms3.png);
}
.item:nth-child(3) {
background-image: url(../../assets/images/home/data_forms2.png);
}
.item:nth-child(4) {
background-image: url(../../assets/images/home/data_forms1.png);
}
.item:nth-child(5) {
background-image: url(../../assets/images/home/data_forms2.png);
}
.item:nth-child(6) {
background-image: url(../../assets/images/home/data_forms1.png);
}
.item {
background-size: 100% 100%;
width: 255px;
display: flex;
align-items: center;
background-color: #f5f5f5;
padding: 20px;
.icon_wrap {
$size: 34px;
$border: 6px;
width: $size;
height: $size;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg-color);
border-radius: 50%;
position: relative;
&::after {
content: "";
width: $size + $border;
height: $size + $border;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: var(--bg-color);
opacity: 0.3;
}
.icon {
font-size: 16px;
color: #fff;
}
.img {
width: 20px;
height: 20px;
}
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.m {
font-weight: bold;
}
.t {
font-size: 12px;
color: #999;
padding-top: 4px;
}
}
}
}
.refund {
color: #ff9731;
font-weight: bold;
}
.table_order_info {
.order_no {
color: #999;
}
.type {
color: #e6a23c;
}
}
.goods_info {
.row {
display: flex;
&:not(:first-child) {
margin-top: 10px;
}
.cover {
width: 40px;
height: 40px;
}
.info {
flex: 1;
display: flex;
flex-direction: column;
padding-left: 10px;
.sku {
color: #999;
}
}
}
}
</style>