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>

View File

@@ -52,6 +52,7 @@
<span>营业</span>
</div>
<div class="u-flex" style="flex-wrap: wrap">
<importData :type="9" style="margin-right: 14px;" />
<el-select v-if="isHeadShop == 1 && loginType == 0" v-model="shopId" placeholder="选择分店"
style="width: 200px; margin-right: 10px;" @change="shopChange">
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
@@ -495,6 +496,7 @@
</template>
<script>
import importData from "@/components/importData/index.vue";
import dataSummaryApi from "@/api/order/data-summary";
import ShopApi from "@/api/account/shop";
import dayjs from "dayjs";
@@ -504,6 +506,7 @@ import { formatDateRange } from './utils/index.js'
import { multiplyAndFormat } from '@/utils/index.js'
export default {
name: "home",
components: { importData },
data() {
return {
multiplyAndFormat,

View File

@@ -44,6 +44,7 @@
<span v-if="!downloadLoading">导出Excel</span>
<span v-else>下载中...</span>
</el-button>
<importData :type="7" />
</el-form-item>
</el-form>
</div>
@@ -189,6 +190,7 @@
<script>
import _ from 'lodash'
import importData from "@/components/importData/index.vue";
import saleSummaryApi from "@/api/order/sale-summary";
import categoryApi from "@/api/product/productclassification";
import ShopApi from "@/api/account/shop";
@@ -197,6 +199,7 @@ import { downloadFile, multiplyAndFormat } from "@/utils/index";
import { formatDateRange } from './utils/index.js'
export default {
components: { importData },
data() {
return {
multiplyAndFormat,

View File

@@ -27,13 +27,14 @@
<el-option v-for="item in branchList" :key="item.shopId" :label="item.shopName" :value="item.shopId" />
</el-select>
</div>
<div>
<div style="display: flex;">
<el-button type="primary" @click="getTableData">查询</el-button>
<el-button @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>
<importData :type="8" />
</div>
</div>
</el-form>
@@ -100,6 +101,7 @@
<script>
import _ from "lodash";
import importData from "@/components/importData/index.vue";
import tableSummaryApi from "@/api/order/table-summary";
import ShopApi from "@/api/account/shop";
import dayjs from "dayjs";
@@ -107,6 +109,7 @@ import { downloadFile } from "@/utils/index";
import { formatDateRange } from './utils/index.js'
export default {
components: { importData },
data() {
return {
timeValue: "today",

View File

@@ -12,7 +12,7 @@
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { ref, onMounted, nextTick, watch } from 'vue'
const props = defineProps({
// {label: '设置',value: 1}
@@ -29,10 +29,8 @@ const modelValue = defineModel('modelValue', {
required: true
})
// 改变索引
function changeHandle(index) {
modelValue.value = index
// 更新active_wrap位置
function updateActivePosition(index) {
let left = 0
itemsWidth.value.forEach((val, i) => {
if (i < index) {
@@ -40,7 +38,12 @@ function changeHandle(index) {
}
})
leftValue.value = left + gap.value * index
}
// 改变索引
function changeHandle(index) {
modelValue.value = index
updateActivePosition(index)
emits('change', index)
}
@@ -57,10 +60,14 @@ onMounted(() => {
console.log('itemRefs===', itemRefs.value);
console.log('itemsWidth===', itemsWidth.value);
changeHandle(modelValue.value)
updateActivePosition(modelValue.value)
})
})
// 监听modelValue变化更新位置
watch(modelValue, (newVal) => {
updateActivePosition(newVal)
})
</script>
<style scoped lang="scss">

View File

@@ -15,6 +15,9 @@
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
</el-tag>
</template> -->
<template #custom>
<importData :type="3" />
</template>
<template #type="scope">
{{ typeFilter(scope.row[scope.prop]) }}
</template>
@@ -170,6 +173,7 @@
</template>
<script setup lang="ts">
import importData from "@/components/importData/index.vue";
import UserAPI from "@/api/product/index";
import { useRouter } from "vue-router";
import type { IObject, IOperatData } from "@/components/CURD/types";
@@ -184,6 +188,8 @@ import searchConfig from "./indexconfig/search";
import MyDialog from "@/components/mycomponents/myDialog.vue";
import Statistics from "./indexconfig/statistics.vue";
const importDataRef = ref(null);
const {
searchRef,
contentRef,
@@ -302,6 +308,11 @@ function getTongji(params: IObject | undefined) {
});
}
// 显示批量导入
function handleUploadClick() {
importDataRef.value.show()
}
// 导出商品
async function handleExportClick() {
try {
@@ -386,6 +397,9 @@ async function handleToolbarClick(name: string) {
let res = await UserAPI.sync();
ElMessage.success("操作成功,数据正在后台同步中...");
}
if (name === "upload") {
importDataRef.value.show()
}
}
async function confirm() {
let res = await UserAPI.stockWarning(form.warnLine);

View File

@@ -85,7 +85,7 @@ const contentConfig: IContentConfig<UserPageQuery> = {
type: "",
name: "export",
auth: "import",
},
}
],
cols: [
// { type: "selection", width: 50, align: "center" },

View File

@@ -84,10 +84,10 @@ export default {
"https://zhyx.eingdong.com/qrcode/api.php?url=https%3A%2F%2Fzhyx.eingdong.com%2Fcopyright%2F%23%2Fpay%3Fid%3D139451580",
pays: [
{
text: "扫",
text: "扫码收款",
},
{
text: "被扫",
text: "二维码收款",
},
],
number: "0",

View File

@@ -23,13 +23,18 @@
</el-tab-pane>
</el-tabs>
<div class="">
<div class="" style="display: flex;">
<el-button icon="plus" @click="addEaraShow()">添加区域</el-button>
<el-button type="primary" icon="plus" @click="addTableShow()">添加台桌</el-button>
<el-button type="danger" icon="Setting" @click="clearTabDialogRef.show()">清台设置</el-button>
<el-button type="primary" icon="download" @click="showDownloadTableCode">
下载桌台码
</el-button>
<!-- 批量导入 -->
<importData :type="5" />
<!-- <el-button icon="Upload" @click="importDataRef.show()">
批量导入
</el-button> -->
<!-- <el-button type="primary" icon="download" @click="downloadShopCpde">
下载店铺码
</el-button> -->
@@ -205,6 +210,7 @@
</template>
<script setup>
import importData from "@/components/importData/index.vue";
import status from "./status.js";
import { useUserStore } from "@/store/modules/user";
const shopUser = useUserStore();

View File

@@ -29,8 +29,11 @@
</div>
<!-- 列表 -->
<page-content ref="contentRef" :content-config="contentConfig" @add-click="handleAddClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @search-click="handleSearchClick"
@edit-click="handleEditClick" @export-click="handleExportClick" @upload-click="" @search-click="handleSearchClick"
@toolbar-click="handleToolbarClick" @operat-click="handleOperatClick" @filter-change="handleFilterChange">
<template #custom>
<importData ref="importDataRef" :type="6" />
</template>
<template #status="scope">
<el-link :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}
@@ -146,6 +149,7 @@
</template>
<script setup>
import importData from "@/components/importData/index.vue";
import UserCouponDialog from "./components/user-coupon-dialog.vue";
import GiveCoupon from "./components/give-coupon.vue";
import usePage from "@/components/CURD/usePage";
@@ -159,6 +163,7 @@ import shopUserApi from "@/api/account/shopUser";
import { useRoute } from 'vue-router'
import { ElNotification } from 'element-plus'
import { downloadFile } from "@/utils/index";
const editMoneyModalRef = ref(null);
const userCouponDialogRef = ref(null);
const GiveCouponRef = ref(null);
@@ -250,6 +255,8 @@ function handleToolbarClick(name) {
console.log(name);
if (name === "custom1") {
ElMessage.success("点击了自定义1按钮");
} else if (name === "upload") {
importDataRef.value.show()
}
}