1.新增批量导入 2.新增财务报表

This commit is contained in:
gyq
2026-02-04 14:51:29 +08:00
parent a5b11cf4f4
commit 76af0f5a83
20 changed files with 1517 additions and 268 deletions

625
src/views/data/finance.vue Normal file
View File

@@ -0,0 +1,625 @@
<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">
<span class="n">{{ tableData.turnover.wechat || 0 }}</span>
<span class="t">微信支付金额</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.alipay || 0 }}</span>
<span class="t">支付宝支付金额</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.selfScan || 0 }}</span>
<span class="t">主扫收款</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.cash || 0 }}</span>
<span class="t">现金收款</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.recharge || 0 }}</span>
<span class="t">充值</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.owed || 0 }}</span>
<span class="t">挂账</span>
</div>
<div class="item">
<span class="n">{{ tableData.turnover.balance || 0 }}</span>
<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">
<span class="n">{{ tableData.sts.avgPayAmount || 0 }}</span>
<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">
<span class="n">{{ tableData.sts.profitAmount || 0 }}</span>
<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">
<span class="n">{{ tableData.sts.productCostAmount || 0 }}</span>
<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">
<span class="n">{{ tableData.sts.netProfitRate || 0 }}</span>
<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">
{{ tableData.discount.newConsumerDiscount || 0 }}
</div>
<div class="label">
新客立减
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.discount.freeCashAmount || 0 }}
</div>
<div class="label">
霸王餐
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.discount.fullMinusAmount || 0 }}
</div>
<div class="label">
满减活动
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.discount.couponAmount || 0 }}
</div>
<div class="label">
优惠券
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.discount.memberDiscount || 0 }}
</div>
<div class="label">
会员折扣
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.discount.pointsDiscountAmount || 0 }}
</div>
<div class="label">
积分抵扣金额
</div>
</div>
<div class="item">
<div class="n">
{{ 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">
{{ tableData.refund.onlineRefundAmount || 0 }}
</div>
<div class="label">
线上退款金额
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.refund.cashRefundAmount || 0 }}
</div>
<div class="label">
现金退款金额
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.refund.rechargeRefundAmount || 0 }}
</div>
<div class="label">
充值退款金额
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.refund.onlineRechargeRefundAmount || 0 }}
</div>
<div class="label">
线上充值退款金额
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.refund.cashRechargeRefundAmount || 0 }}
</div>
<div class="label">
现金充值退款金额
</div>
</div>
<div class="item">
<div class="n">
{{ tableData.refund.memberRefundAmount || 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;
.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;
}
.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;
}
.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>