Files
cashier-web/src/views/data/finance.vue
2026-02-05 09:10:45 +08:00

690 lines
19 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="gyq_container">
<div class="row">
<div class="between">
<el-form :model="queryForm" inline>
<el-form-item>
<el-date-picker v-model="queryForm.queryDate" type="date" placeholder="选择日期" format="YYYY-MM-DD"
value-format="YYYY-MM-DD" :disabled-date="disabledFutureDate" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" :loading="loading" @click="handleQuery">查询</el-button>
<el-button @click="handleReset" icon="Refresh" :loading="loading">重置</el-button>
</el-form-item>
</el-form>
<el-form inline>
<el-form-item>
<el-select v-model="queryForm.platform" placeholder="选择平台" clearable style="width: 150px;">
<el-option v-for="item in platformList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleExport" icon="Download">导出</el-button>
</el-form-item>
</el-form>
</div>
</div>
<div class="row" v-loading="loading">
<div class="warp" style="width: 400px;">
<div class="card">
<div class="header_title">营业额</div>
<div class="num" style="color: var(--el-color-primary);">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.turnover || 0) }}
</div>
<div class="intro">营业额</div>
<div class="pay_wrap">
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.wechat || 0) }}
</div>
<span class="t">微信支付金额</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.alipay || 0) }}
</div>
<span class="t">支付宝支付金额</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.selfScan || 0) }}
</div>
<span class="t">二维码收款</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.barScan || 0) }}
</div>
<span class="t">扫码收款</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.cash || 0) }}
</div>
<span class="t">现金收款</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.recharge || 0) }}
</div>
<span class="t">充值</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.owed || 0) }}
</div>
<span class="t">挂账</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.turnover.balance || 0) }}
</div>
<span class="t">余额支付</span>
</div>
</div>
<!-- <div class="order_info">
<div class="order_title">
<span class="dot"></span>
<span class="t">订单</span>
</div>
<div class="order_lits">
<div class="item">
<span class="t">订单金额</span>
<span class="n">{{ tableData.order.orderAmount || 0 }}</span>
</div>
<span class="line">|</span>
<div class="item">
<span class="t">订单总数</span>
<span class="n">{{ tableData.order.orderCount || 0 }}</span>
</div>
</div>
</div> -->
</div>
<div class="card">
<div class="header_title">数据统计</div>
<div class="pay_wrap">
<div class="item">
<span class="n">{{ tableData.sts.customerCount || 0 }}</span>
<span class="t">就餐人数</span>
</div>
<div class="item">
<span class="n">{{ tableData.sts.orderCount || 0 }}</span>
<span class="t">订单数</span>
</div>
<div class="item">
<span class="n">{{ tableData.sts.tableCount || 0 }}</span>
<span class="t">桌台数</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.sts.avgPayAmount || 0) }}
</div>
<div class="t">客单价
<el-tooltip class="box-item" effect="dark" content="实付金额(包含现金支付 包含会员支付 包含挂账)/就餐人数没有具体人数时默认一桌按照1人计算"
placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</div>
<div class="item">
<span class="n">{{ tableData.sts.turnoverRate || 0 }}%</span>
<div class="t">翻台率
<el-tooltip class="box-item" effect="dark" content="(订单数-桌台数)/桌台数*100%" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.sts.profitAmount || 0) }}
</div>
<div class="t">毛利润
<el-tooltip class="box-item" effect="dark" content="(订单实付金额-商品成本)" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.sts.productCostAmount || 0) }}
</div>
<span class="t">商品成本</span>
</div>
<div class="item">
<span class="n">{{ tableData.sts.profitRate || 0 }}%</span>
<div class="t">毛利率
<el-tooltip class="box-item" effect="dark" content="(订单实付金额-商品成本)/订单实付金额*100%" placement="top">
<el-icon color="#666">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
</div>
<!-- <div class="item">
<span class="n">{{ tableData.sts.netProfitAmount || 0 }}%</span>
<span class="t">净利率</span>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.sts.netProfitRate || 0) }}
</div>
<span class="t">净利润</span>
</div> -->
</div>
</div>
</div>
<div class="card" style="flex: 1;">
<div class="header_title">商家经营数据</div>
<div class="content">
<div class="card" style="flex: 1;">
<div class="num" style="color: var(--el-color-success);">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.discountAmount || 0) }}
</div>
<div class="intro">优惠金额</div>
<div class="item_list">
<div class="item">
<div class="n">
{{ tableData.discount.discountCount || 0 }}
</div>
<div class="label">
优惠笔数
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.newConsumerDiscount || 0) }}
</div>
<div class="label">
新客立减
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.freeCashAmount || 0) }}
</div>
<div class="label">
霸王餐
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.fullMinusAmount || 0) }}
</div>
<div class="label">
满减活动
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.couponAmount || 0) }}
</div>
<div class="label">
优惠券
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.memberDiscount || 0) }}
</div>
<div class="label">
会员折扣
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.pointsDiscountAmount || 0) }}
</div>
<div class="label">
积分抵扣金额
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.discount.orderDiscount || 0) }}
</div>
<div class="label">
订单改价
</div>
</div>
</div>
</div>
<div class="card" style="flex: 1;">
<div class="num" style="color: var(--el-color-danger);">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.refundAmount || 0) }}
</div>
<div class="intro">退款金额</div>
<div class="item_list">
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.onlineRefundAmount || 0) }}
</div>
<div class="label">
线上退款金额
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.cashRefundAmount || 0) }}
</div>
<div class="label">
现金退款金额
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.memberRefundAmount || 0) }}
</div>
<div class="label">
余额退款金额
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.onlineRechargeRefundAmount || 0) }}
</div>
<div class="label">
线上充值退款金额
</div>
</div>
<div class="item">
<div class="n">
<span class="i"></span>
{{ multiplyAndFormat(tableData.refund.cashRechargeRefundAmount || 0) }}
</div>
<div class="label">
现金充值退款金额
</div>
</div>
</div>
</div>
<div class="card" style="flex: 1;">
<div class="num" style="color: var(--el-color-warning);">
<span class="i"></span>
{{ multiplyAndFormat(tableData.order.orderAmount || 0) }}
</div>
<div class="intro">订单金额</div>
<div class="item_list">
<div class="item">
<div class="n">
{{ tableData.order.orderCount || 0 }}
</div>
<div class="label">
订单总数
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import dayjs from "dayjs";
import { ref, onMounted } from "vue";
import OrderApi from "@/api/order/order";
import { downloadFile, multiplyAndFormat } from '@/utils'
const queryForm = ref({
queryDate: dayjs().format('YYYY-MM-DD'), // 查询日期 yyyy-MM-dd
platform: 'czg',
shopId: localStorage.getItem('shopId') || '',
mainShopId: ''
});
const platformList = ref([
{ value: 'czg', label: '银收客' },
{ value: 'MTuan', label: '美团' },
{ value: 'CMeMe', label: '菜么么' },
{ value: 'KRuYun', label: '客如云' },
])
// 禁用今天以后的日期
const disabledFutureDate = (time) => {
return dayjs(time).isAfter(dayjs().startOf('day'))
}
// 查询
function handleQuery() {
getData()
}
// 重置
function handleReset() {
queryForm.value.queryDate = dayjs().format('YYYY-MM-DD')
queryForm.value.platform = 'czg'
getData()
}
// 导出
async function handleExport() {
try {
if (!queryForm.value.platform) {
ElMessage.error('请选择导出平台')
return
}
const res = await OrderApi.financeExport(queryForm.value)
downloadFile(res, '财务报表', 'xlsx')
} catch (error) {
console.log(error);
}
}
const loading = ref(false);
const tableData = ref({
turnover: {},
order: {},
discount: {},
refund: {},
sts: {}
});
// 查询财务报表
async function getData() {
try {
loading.value = true
const res = await OrderApi.financeSts(queryForm.value)
tableData.value = res
console.log('tableData.value', tableData.value);
} catch (error) {
console.log(error);
}
setTimeout(() => {
loading.value = false
}, 500);
}
onMounted(() => {
getData()
})
</script>
<style scoped lang="scss">
.gyq_container {
padding: 14px;
.gyq_content {
padding: 14px;
background-color: #fff;
border-radius: 8px;
}
}
.between {
flex: 1;
display: flex;
justify-content: space-between;
}
.row {
display: flex;
gap: 14px;
&.mt14 {
margin-top: 14px;
}
}
.warp {
display: flex;
flex-direction: column;
gap: 14px;
}
.card {
border-radius: 12px;
background-color: #fff;
padding: 20px;
.header_title {
font-size: 16px;
color: 333;
font-weight: bold;
}
.content {
background-color: #F8F8F8;
border-radius: 8px;
margin-top: 20px;
padding: 14px;
display: flex;
gap: 14px;
.item_list {
margin-top: 14px;
.title {
font-size: 14px;
color: #666;
}
.item {
height: 50px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 14px;
&:nth-child(odd) {
background-color: #F3F7FA;
}
.n {
font-size: 16px;
color: #333;
font-weight: bold;
display: flex;
align-items: center;
.i {
font-size: 10px;
position: relative;
top: 1px;
}
}
.label {
font-size: 14px;
color: #666;
}
}
}
}
.num {
font-size: 24px;
font-weight: bold;
display: flex;
align-items: center;
.i {
font-size: 12px;
position: relative;
top: 3px;
}
}
.intro {
font-size: 14px;
color: #999;
}
.pay_wrap {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: 0px;
grid-row-gap: 14px;
background-color: #F8F8F8;
padding: 14px;
margin-top: 14px;
border-radius: 8px;
.item {
display: flex;
flex-direction: column;
.n {
font-size: 20px;
font-weight: bold;
color: #333;
display: flex;
align-items: center;
.i {
font-size: 12px;
position: relative;
top: 3px;
}
}
.t {
font-size: 14px;
color: #666;
display: flex;
align-items: center;
gap: 4px;
}
}
}
.order_info {
border-top: 1px solid #ececec;
padding-top: 14px;
.order_title {
display: flex;
gap: 10px;
align-items: center;
.dot {
--size: 12px;
width: var(--size);
height: var(--size);
border-radius: 50%;
background-color: var(--el-color-danger);
}
.t {
font-size: 16px;
color: #333;
font-weight: bold;
}
}
.order_lits {
display: flex;
gap: 14px;
margin-top: 4px;
.line {
color: #ececec;
}
.item {
display: flex;
align-items: center;
gap: 14px;
.t {
font-size: 14px;
color: #666;
}
.n {
font-size: 16px;
color: #333;
font-weight: bold;
}
}
}
}
}
.waterfall {
/* 核心属性1定义瀑布流列数关键 */
column-count: 3;
/* 核心属性2列之间的间距替代margin避免卡片间距错乱 */
column-gap: 14px;
/* 可选:防止卡片内容被列分割(关键!避免卡片跨列断裂) */
break-inside: avoid;
.waterfall-card {
/* 必须:适配多列布局,避免卡片宽度溢出 */
width: 100%;
/* 卡片间距仅需设置底部外边距上下间距左右间距由column-gap控制 */
margin-bottom: 14px;
padding: 24px;
background-color: #fff;
border-radius: 12px;
/* 配合break-inside: avoid强化卡片不可分割 */
page-break-inside: avoid;
.header-title {
display: flex;
gap: 14px;
align-items: center;
padding-bottom: 10px;
.dot {
--size: 10px;
width: var(--size);
height: var(--size);
border-radius: 50%;
}
.t {
font-size: 24px;
color: #000;
font-weight: bold;
}
}
p {
margin-top: 10px;
font-size: 14px;
color: #666;
padding-left: 24px;
}
}
}
.tips {
margin-top: 6px;
font-size: 14px;
color: #999;
padding-left: 24px;
}
</style>