Browse Source

Merge branch 'master' into dev

tags/小炒逻辑变更前
txb 2 years ago
parent
commit
5ddb75d645
25 changed files with 2490 additions and 0 deletions
  1. +54
    -0
      config/routes.js
  2. +245
    -0
      src/pages/order/cost-of-sales/index.jsx
  3. +36
    -0
      src/pages/order/cost-of-sales/index.less
  4. +40
    -0
      src/pages/order/cost-of-sales/service.js
  5. +149
    -0
      src/pages/order/gross-profit-store-sales/index.jsx
  6. +36
    -0
      src/pages/order/gross-profit-store-sales/index.less
  7. +21
    -0
      src/pages/order/gross-profit-store-sales/service.js
  8. +591
    -0
      src/pages/order/order-market-report/index.jsx
  9. +41
    -0
      src/pages/order/order-market-report/index.less
  10. +26
    -0
      src/pages/order/order-market-report/service.js
  11. +28
    -0
      src/pages/order/order-member-statement/index.jsx
  12. +16
    -0
      src/pages/order/order-member-statement/index.less
  13. +28
    -0
      src/pages/order/order-product-report/index.jsx
  14. +16
    -0
      src/pages/order/order-product-report/index.less
  15. +245
    -0
      src/pages/order/order-report-detail/index.jsx
  16. +201
    -0
      src/pages/order/order-report-detail/index.less
  17. +9
    -0
      src/pages/order/order-report-detail/service.js
  18. +299
    -0
      src/pages/order/order-report/index.jsx
  19. +36
    -0
      src/pages/order/order-report/index.less
  20. +25
    -0
      src/pages/order/order-report/service.js
  21. +28
    -0
      src/pages/order/order-revenue-statement/index.jsx
  22. +16
    -0
      src/pages/order/order-revenue-statement/index.less
  23. +229
    -0
      src/pages/order/sales-gross-profit/index.jsx
  24. +36
    -0
      src/pages/order/sales-gross-profit/index.less
  25. +39
    -0
      src/pages/order/sales-gross-profit/service.js

+ 54
- 0
config/routes.js View File

@@ -586,6 +586,60 @@ export default [
component: './order/exOrder',
access: 'k31',
},
{
name: '营销报表',
path: '/order/order-market-report',
component: './order/order-market-report',
access: 'k31',
},
{
name: '订单报表',
path: '/order/order-report',
component: './order/order-report',
access: 'k31',
},
{
name: '订单报表详情',
path: '/order/order-report/order-report-detail',
component: './order/order-report-detail',
access: 'k31',
},
{
name: '营收报表',
path: '/order/order-revenue-statement',
component: './order/order-revenue-statement',
access: 'k31',
},
{
name: '产品报表',
path: '/order/order-product-report',
component: './order/order-product-report',
access: 'k31',
},
{
name: '会员报表',
path: '/order/order-member-statement',
component: './order/order-member-statement',
access: 'k31',
},
{
name: '销售成本分析',
path: '/order/cost-of-sales',
component: './order/cost-of-sales',
access: 'k31',
},
{
name: '销售毛利分析',
path: '/order/sales-gross-profit',
component: './order/sales-gross-profit',
access: 'k31',
},
{
name: '门店销售毛利分析',
path: '/order/gross-profit-store-sales',
component: './order/gross-profit-store-sales',
access: 'k31',
},
],
},
{


+ 245
- 0
src/pages/order/cost-of-sales/index.jsx View File

@@ -0,0 +1,245 @@
import React, { useState, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker, Table, Pagination, Spin, Select, message } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
import costSalesAPI from "./service";
import moment from 'moment';

const columns = [
{
title: '门店名称',
dataIndex: 'storeId',
key: 'storeId',
},
{
title: '商品名称',
dataIndex: 'goodsId',
key: 'goodsId',
},
{
title: '销售收入',
dataIndex: 'salesPrice',
key: 'salesPrice',
},
{
title: '销售成本',
dataIndex: 'costPrice',
key: 'costPrice',
},
{
title: '销售数量',
dataIndex: 'salesNum',
key: 'salesNum',
},
{
title: '成本率',
dataIndex: 'costRate',
key: 'costRate',
}
];

const LoadingCard = () => {
return (
<div className={styles['loading-card']}>
<Spin size="large" />
</div>
)
}

/**
* 销售成本分析
* @returns
*/
export default function Index() {

//订单报表列表
const [costSalesData, setCostSalesData] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [showLoading, setShowLoading] = useState(false);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]); //日期选择器
//门店
const [storeIdArray, setStoreIdArray] = useState([]);
const [storeSelect, setStoreSelect] = useState([]);
//商品
const [goodsIdArray, setGoodsIdArray] = useState([]);
const [goodsIdSelect, setGoodsIdSelect] = useState([]);
//商品类型
const [goodsTypeArray, setGoodsTypeArray] = useState([]);
const [goodsTypeSelect, setGoodsTypeSelect] = useState([]);

//页码变化
const onPageChange = (current, pageSize) => {
setCurrent(current);
setPageSize(pageSize);
}

//查询
const onQueryReportSalescost = async () => {
const jsonData = {
"storeId": storeIdArray,
"goodsId": goodsIdArray,
"goodsTypeId": goodsTypeArray,
"begintime": timeRange[0],
"endtime": timeRange[1],
current,
pageSize
}
setShowLoading(true);
const response = await costSalesAPI.getReportSalescost({});
setShowLoading(false);
if (response.statusCode === 200) {
setCostSalesData(response.data.data);
} else {
message.error(response.errors || '获取销售成本失败');
}
}

/**
* 查询店铺列表
*/
const onQueryStoreList = async () => {
setShowLoading(true);
const response = await costSalesAPI.gettree({});
setShowLoading(false);
if (response.statusCode === 200) {
const storeList = [];
response.data.forEach(item => {
if (item.type === 2 || item.type === 3) {
storeList.push(item)
}
})
setStoreSelect(storeList);
} else {
message.error('查询店铺列表失败');
}
}

//查询商品列表
const onQueryGoodsList = async () => {
setShowLoading(true);
const response = await costSalesAPI.goodsList({});
setShowLoading(false);
if (response.statusCode === 200) {
setGoodsIdSelect(response.data);
} else {
message.error('查询商品列表失败');
}
}

//查询商品分类类型列表
const onQueryGoodsType = async () => {
setShowLoading(true);
const response = await costSalesAPI.goodsTypeList({});
setShowLoading(false);
if (response.statusCode === 200) {
setGoodsTypeSelect(response.data);
} else {
message.error('查询商品分类列表失败');
}
}

useEffect(() => {
onQueryReportSalescost();
onQueryStoreList();
onQueryGoodsList();
onQueryGoodsType();
}, []);

return (
<PageContainer>
{showLoading ? <LoadingCard></LoadingCard> : null}
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<div className={styles['data-search-left']}>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择门店"
onChange={(values) => setStoreIdArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
storeSelect.map( (item) => {
return (
<Option key={item.key}>{item.title}</Option>
)
})
}
</Select>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择商品"
onChange={(values) => setGoodsIdArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
goodsIdSelect.map( (item, index) => {
return (
<Option key={item.id}>{item.name}</Option>
)
})
}
</Select>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择商品类别"
onChange={(values) => setGoodsTypeArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
goodsTypeSelect.map( (item) => {
return (
<Option key={item.id}>{item.name}</Option>
)
})
}
</Select>
<RangePicker size='middle' className={styles['my-range-picker']} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
]
setTimeRange(tempDate);
}} />
</div>
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary" onClick={onQueryReportSalescost}>查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
<Table dataSource={costSalesData} columns={columns} pagination={false} />
<div className={styles['table-page']}>
<Pagination current={current} pageSize={pageSize} total={total} onChange={onPageChange} />
</div>
</Card>
</PageContainer>
)
}

+ 36
- 0
src/pages/order/cost-of-sales/index.less View File

@@ -0,0 +1,36 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-page {
display: flex;
justify-content: flex-end;
align-items: center;
height: 50px;
}

// 加载中
.loading-card {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}

+ 40
- 0
src/pages/order/cost-of-sales/service.js View File

@@ -0,0 +1,40 @@
import { request } from 'umi';

export default {
getReportSalescost(data) {
return request(`/kitchen/api/report/salescost`, {
method: 'Post',
data: data,
});
},
/**
* 查询商品信息
*/
goodsList(data) {
return request(`/kitchen/api/goodes/list`, {
method: 'Post',
data: data,
});
},
/**
* 查询商品类型
*/
goodsTypeList(data) {
return request(`/kitchen/api/goodstype/list`, {
method: 'Post',
data: data,
});
},

/**
* 查询门店列表
*/
gettree(params) {
return request('​/kitchen/api​/sysOrg​/tree', {
data: {
...params,
},
});
}

};

+ 149
- 0
src/pages/order/gross-profit-store-sales/index.jsx View File

@@ -0,0 +1,149 @@
import React, { useState, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker, Table, Pagination, Spin, Select, message } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
import costSalesAPI from "./service";
import moment from 'moment';

const columns = [
{
title: '门店名称',
dataIndex: 'storeId',
key: 'storeId',
},
{
title: '毛利率',
dataIndex: 'marginRatio',
key: 'marginRatio',
}
];

const LoadingCard = () => {
return (
<div className={styles['loading-card']}>
<Spin size="large" />
</div>
)
}

/**
* 门店销售毛利分析
* @returns
*/
export default function Index() {

//订单报表列表
const [costSalesData, setCostSalesData] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [showLoading, setShowLoading] = useState(false);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]); //日期选择器
//门店
const [storeIdArray, setStoreIdArray] = useState([]);
const [storeSelect, setStoreSelect] = useState([]);

//页码变化
const onPageChange = (current, pageSize) => {
setCurrent(current);
setPageSize(pageSize);
}

//查询
const onQueryReportSalescost = async () => {
const jsonData = {
"storeId": storeIdArray,
"begintime": timeRange[0],
"endtime": timeRange[1],
current,
pageSize
}
setShowLoading(true);
const response = await costSalesAPI.getReportOrgsalesmargin({});
setShowLoading(false);
if (response.statusCode === 200) {
setCostSalesData(response.data.data);
} else {
message.error(response.errors || '获取销售成本失败');
}
}

/**
* 查询店铺列表
*/
const onQueryStoreList = async () => {
setShowLoading(true);
const response = await costSalesAPI.gettree({});
setShowLoading(false);
if (response.statusCode === 200) {
const storeList = [];
response.data.forEach(item => {
if (item.type === 2 || item.type === 3) {
storeList.push(item)
}
})
setStoreSelect(storeList);
} else {
message.error('查询店铺列表失败');
}
}

useEffect(() => {
onQueryReportSalescost();
onQueryStoreList();
}, []);

return (
<PageContainer>
{showLoading ? <LoadingCard></LoadingCard> : null}
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<div className={styles['data-search-left']}>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择门店"
onChange={(values) => setStoreIdArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
storeSelect.map( (item) => {
return (
<Option key={item.key}>{item.title}</Option>
)
})
}
</Select>
<RangePicker size='middle' className={styles['my-range-picker']} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
]
setTimeRange(tempDate);
}} />
</div>
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary" onClick={onQueryReportSalescost}>查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
<Table dataSource={costSalesData} columns={columns} pagination={false} />
<div className={styles['table-page']}>
<Pagination current={current} pageSize={pageSize} total={total} onChange={onPageChange} />
</div>
</Card>
</PageContainer>
)
}

+ 36
- 0
src/pages/order/gross-profit-store-sales/index.less View File

@@ -0,0 +1,36 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-page {
display: flex;
justify-content: flex-end;
align-items: center;
height: 50px;
}

// 加载中
.loading-card {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}

+ 21
- 0
src/pages/order/gross-profit-store-sales/service.js View File

@@ -0,0 +1,21 @@
import { request } from 'umi';

export default {
getReportOrgsalesmargin(data) {
return request(`/kitchen/api/report/orgsalesmargin`, {
method: 'Post',
data: data,
});
},
/**
* 查询门店列表
*/
gettree(params) {
return request('​/kitchen/api​/sysOrg​/tree', {
data: {
...params,
},
});
}
};

+ 591
- 0
src/pages/order/order-market-report/index.jsx View File

@@ -0,0 +1,591 @@
import React, { useEffect, useState } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { DownloadOutlined } from '@ant-design/icons';
import { Button, Table, Card, DatePicker, message, Pagination, Spin, TreeSelect } from 'antd';
import styles from './index.less';
import moment from 'moment';
import marketAPI from "./service";
import ExportJsonExcel from "js-export-excel"

const { TreeNode } = TreeSelect;
const { RangePicker } = DatePicker;
const columns = [
{
title: '时间',
dataIndex: 'date',
key: 'date',
width: 170,
align: 'center',
fixed: 'left'
},
{
title: '店铺',
dataIndex: 'shopName',
key: 'shopName',
width: 170,
align: 'center',
fixed: 'left',
filters: [
{
text: '四川黑菠萝科技',
value: '四川黑菠萝科技',
},
{
text: '四川白菠萝科技',
value: '四川白菠萝科技',
}
],
onFilter: (value, record) => record.shopName.indexOf(value) === 0,
},
{
title: '营收',
dataIndex: 'revenue',
key: 'revenue',
children: [
{
title: '营业额',
dataIndex: 'turnover',
key: 'turnover',
children: [
{
title: '流水',
dataIndex: 'revenueFlow',
key: 'revenueFlow',
width: 100,
align: 'center',
},
{
title: '实收',
dataIndex: 'paidAmount',
key: 'paidAmount',
width: 100,
align: 'center',
},
]
},
{
title: '优惠明细',
dataIndex: 'discountDetails',
key: 'discountDetails',
children: [
{
title: '会员价',
dataIndex: 'discountMember',
key: 'discountMember',
width: 100,
align: 'center',
},
{
title: '优惠券',
dataIndex: 'discountCoupon',
key: 'discountCoupon',
width: 100,
align: 'center',
},
{
title: '活动',
dataIndex: 'discountActivity',
key: 'discountActivity',
width: 100,
align: 'center',
}
]
},
]
},
{
title: '分类营收',
dataIndex: 'classifiedRevenue',
key: 'classifiedRevenue',
children: [
{
title: '外卖营业额',
dataIndex: 'takeOutTurnover',
key: 'takeOutTurnover',
children: [
{
title: '外卖营收',
dataIndex: 'takeOutRevenue',
key: 'takeOutRevenue',
width: 100,
align: 'center',
},
{
title: '美团外卖',
dataIndex: 'meituanTakeout',
key: 'meituanTakeout',
width: 100,
align: 'center',
},
{
title: '饿了么外卖',
dataIndex: 'hungryTakeOut',
key: 'hungryTakeOut',
width: 100,
align: 'center',
},
]
},
{
title: '食堂营收',
dataIndex: 'canteenRevenue',
key: 'canteenRevenue',
children: [
{
title: '堂食流水',
dataIndex: 'freshWaterInTheHall',
key: 'freshWaterInTheHall',
width: 100,
align: 'center',
},
{
title: '堂食实收',
dataIndex: 'paidInFood',
key: 'paidInFood',
width: 100,
align: 'center',
}
]
},
{
title: '第三方平台营收',
dataIndex: 'thirdPartyPlatformRevenue',
key: 'thirdPartyPlatformRevenue',
children: [
{
title: '大众点评',
dataIndex: 'publicComments',
key: 'publicComments',
width: 100,
align: 'center',
},
{
title: '口碑',
dataIndex: 'wordOfMouth',
key: 'wordOfMouth',
width: 100,
align: 'center',
},
{
title: '抖音',
dataIndex: 'tiktok',
key: 'tiktok',
width: 100,
align: 'center',
}
]
},
]
},
{
title: '堂食单价',
dataIndex: 'unitPriceCanteenFood',
key: 'unitPriceCanteenFood',
children: [
{
title: '客单价',
dataIndex: 'customerUnitPrice',
key: 'customerUnitPrice',
children: [
{
title: '流水单价',
dataIndex: 'customerRevenueFlow',
key: 'customerRevenueFlow',
width: 100,
align: 'center',
},
{
title: '实收单价',
dataIndex: 'customerPaidAmount',
key: 'customerPaidAmount',
width: 100,
align: 'center',
}
]
},
{
title: '单单价',
dataIndex: 'singleUnitPrice',
key: 'singleUnitPrice',
children: [
{
title: '流水单价',
dataIndex: 'orderRevenueFlow',
key: 'orderRevenueFlow',
width: 100,
align: 'center',
},
{
title: '实收单价',
dataIndex: 'orderPaidAmount',
key: 'orderPaidAmount',
width: 100,
align: 'center',
}
]
},
]
},
{
title: '交易笔数',
dataIndex: 'numberOfTransactions',
key: 'numberOfTransactions',
children: [
{
title: '现金',
dataIndex: 'sourceCashCount',
key: 'sourceCashCount',
width: 100,
align: 'center',
},
{
title: '微信支付',
dataIndex: 'sourceWeChatCount',
key: 'sourceWeChatCount',
width: 100,
align: 'center',
},
{
title: '大众点评',
dataIndex: 'numPublicComments',
key: 'numPublicComments',
width: 100,
align: 'center',
},
{
title: '口碑',
dataIndex: 'numWordOfMouth',
key: 'numWordOfMouth',
width: 100,
align: 'center',
},
{
title: '抖音',
dataIndex: 'numTiktok',
key: 'numTiktok',
width: 100,
align: 'center',
},
{
title: '美团外卖',
dataIndex: 'numMeituanTakeout',
key: 'numMeituanTakeout',
width: 100,
align: 'center',
},
{
title: '饿了么外卖',
dataIndex: 'numHungryTakeOut',
key: 'numHungryTakeOut',
width: 100,
align: 'center',
},
]
},
{
title: '收入构成',
dataIndex: 'revenueComposition',
key: 'revenueComposition',
children: [
{
title: '收入来源',
dataIndex: 'sourceOfIncome',
key: 'sourceOfIncome',
children: [
{
title: '会员',
dataIndex: 'memberIncomeMoney',
key: 'memberIncomeMoney',
width: 100,
align: 'center',
},
{
title: '非会员',
dataIndex: 'nonMemberIncomeMoney',
key: 'nonMemberIncomeMoney',
width: 100,
align: 'center',
},
{
title: '外卖',
dataIndex: 'incomeTakeOut',
key: 'incomeTakeOut',
width: 100,
align: 'center',
},
{
title: '团购',
dataIndex: 'incomeGroupPurchase',
key: 'incomeGroupPurchase',
width: 100,
align: 'center',
},
]
},
{
title: '收入结构',
dataIndex: 'revenueStructure',
key: 'revenueStructure',
children: [
{
title: '微信支付',
dataIndex: 'sourceWeChatMoney',
key: 'sourceWeChatMoney',
width: 100,
align: 'center',
},
{
title: '现金支付',
dataIndex: 'sourceCashMoney',
key: 'sourceCashMoney',
width: 100,
align: 'center',
},
{
title: '大众点评',
dataIndex: 'incomePublicComments',
key: 'incomePublicComments',
width: 100,
align: 'center',
},
{
title: '口碑',
dataIndex: 'incomeWordOfMouth',
key: 'incomeWordOfMouth',
width: 100,
align: 'center',
},
{
title: '抖音',
dataIndex: 'incomeTiktok',
key: 'incomeTiktok',
width: 100,
align: 'center',
},
{
title: '美团外卖',
dataIndex: 'incomeMeituan',
key: 'incomeMeituan',
width: 100,
align: 'center',
},
{
title: '饿了么外卖',
dataIndex: 'incomeHungry',
key: 'incomeHungry',
width: 100,
align: 'center',
},
]
},
]
}
]

const LoadingCard = () => {
return (
<div className={styles['loading-card']}>
<Spin size="large" />
</div>
)
}

/**
* 营销报表
* @returns
*/
export default function Index() {

const [data, setData] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [orgTree, setOrgTree] = useState([]);
const [showLoading, setShowLoading] = useState(false);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]); //日期选择器
const [currentOrg, setCurrentOrg] = useState('');

useEffect(() => {
queryMarketReport();
}, [current, pageSize, timeRange]);

useEffect(() => {
onGetOrgTree();
}, []);

//计算总计
const calcTotal = () => {
const originData = JSON.parse(JSON.stringify(data));
const totalData = {}
data.forEach(dataObj => {
Object.keys(dataObj).forEach(key => {
if (key === 'date') {
totalData.date = '合计';
} else if (key == 'shopName') {
totalData.shopName = '';
} else if (typeof dataObj[key] === 'number') {
if (totalData[key] && typeof totalData[key] === 'number') {
totalData[key] += dataObj[key];
} else {
totalData[key] = 0;
totalData[key] += dataObj[key];
}
} else {
totalData[key] = '暂无统计'
}
});
});
originData.push(totalData);
setData(originData);
}

const queryMarketReport = async () => {
const jsonData = {
startTime: timeRange[0],
endTime: timeRange[1],
current: current,
pageSize: pageSize
}
currentOrg.key && (jsonData.shopId = currentOrg.key);
setShowLoading(true);
const response = await marketAPI.getMarketReportList(jsonData);
setShowLoading(false);
if (response.statusCode === 200) {
setData(response.data.data);
setTotal(response.data.total);
} else {
message.error(response.errors || '获取营销报表失败');
}
}

//页码变化
const onPageChange = (current, pageSize) => {
setCurrent(current);
setPageSize(pageSize);
}

//下载Excel
const onDownloadExcel = async () => {
const jsonData = {
startTime: timeRange[0],
endTime: timeRange[1],
}
currentOrg.key && (jsonData.shopId = currentOrg.key);
setShowLoading(true);
const response = await marketAPI.marketingReportExport(jsonData);
setShowLoading(false);
if (response.statusCode === 200) {
let option = {}; //option代表的就是excel文件
const date = new Date();
const excelName = "营销报表-" + date.toLocaleString().replaceAll('\/', '-');
option.fileName = excelName; //excel文件名称
const sheetHeader = [];
Object.keys(response.data[0]).forEach(item => {
sheetHeader.push(item);
});
console.log('sheetHeader', sheetHeader);
option.datas = [
{
sheetData: response.data, //excel文件中的数据源
sheetName: excelName, //excel文件中sheet页名称
sheetFilter: sheetHeader, //excel文件中需显示的列数据
sheetHeader: ['ID', '时间', '店铺名称', '店铺ID', '营收流水',
'实际营收', '会员折扣', '优惠券折扣', '活动折扣','客单价-流水单价', '客单价-实收单价',
'单单价-总流水', '单单价-总实收',
'现金-交易笔数', '现金收入', '微信-交易笔数', '微信收入', '会员收入', '非会员收入'
] //excel文件中每列的表头名称
}
]
let toExcel = new ExportJsonExcel(option); //生成excel文件
toExcel.saveExcel();
} else {
message.error(response.errors || '导出失败');
}
}

//获取组织树
const onGetOrgTree = async () => {
setShowLoading(true);
const response = await marketAPI.getOrgTree();
setShowLoading(false);
if (response.statusCode === 200) {
const originTree = response.data;
onSetOrgTreeStatus(originTree);
setOrgTree(originTree);
} else {
message.error(response.errors || '获取组织树出错');
}
}

//设置组织树不可选择状态
const onSetOrgTreeStatus = (originTree) => {
originTree.forEach(treeItem => {
if (treeItem.children && treeItem.children.length > 0) {
onSetOrgTreeStatus(treeItem.children);
} else {
if (treeItem.type === 2 || treeItem.type === 3) {
treeItem.disabled = false;
} else {
treeItem.disabled = true;
}
}
});
}

return (
<PageContainer>
{showLoading ? <LoadingCard></LoadingCard> : null}
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<div className={styles['data-search-left']}>
<RangePicker className={styles['my-range-picker']} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
]
setTimeRange(tempDate);
}} />
<TreeSelect
style={{
width: '300px',
marginLeft: '50px'
}}
dropdownStyle={{
maxHeight: 400,
overflow: 'auto',
}}
treeData={orgTree}
onSelect={(value, node) => {
if (node.type === 2 || node === 3) {
setCurrentOrg(node);
} else {
setCurrentOrg("");
}
}}
placeholder="请选择组织架构"
treeDefaultExpandAll
/>
</div>

<div className={styles['data-search-btns']}>
<Button type="primary" icon={<DownloadOutlined />} size='middle' onClick={onDownloadExcel}>
下载
</Button>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary" onClick={queryMarketReport}>查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
<Table dataSource={data} columns={columns} bordered={true} sticky={true} scroll={{ x: 1000 }} pagination={false}>
</Table>
<div className={styles['table-page']}>
<Pagination current={current} pageSize={pageSize} total={total} onChange={onPageChange} />
</div>
</Card>
</PageContainer>
)
}

+ 41
- 0
src/pages/order/order-market-report/index.less View File

@@ -0,0 +1,41 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-page {
display: flex;
justify-content: flex-end;
align-items: center;
height: 50px;
}

.data-search-left {
display: flex;
align-items: center;
}

// 加载中
.loading-card {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}

+ 26
- 0
src/pages/order/order-market-report/service.js View File

@@ -0,0 +1,26 @@
import { request } from 'umi';

export default {
//获取分页数据
getMarketReportList(data) {
return request(`/kitchen/api/report-statistics/marketing-report`, {
method: 'Post',
data: data,
});
},

//获取组织架构
getOrgTree() {
return request(`/kitchen/api/report-statistics/org-tree`, {
method: 'GET',
});
},

//下载报表
marketingReportExport(data) {
return request(`/kitchen/api/report-statistics/marketing-report-export`, {
method: 'Post',
data: data,
});
}
};

+ 28
- 0
src/pages/order/order-member-statement/index.jsx View File

@@ -0,0 +1,28 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
/**
* 会员报表
* @returns
*/
export default function Index() {

return (
<PageContainer>
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<RangePicker />
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary">查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
会员报表
</Card>
</PageContainer>
)
}

+ 16
- 0
src/pages/order/order-member-statement/index.less View File

@@ -0,0 +1,16 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-card {
}

+ 28
- 0
src/pages/order/order-product-report/index.jsx View File

@@ -0,0 +1,28 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
/**
* 产品报表
* @returns
*/
export default function Index() {

return (
<PageContainer>
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<RangePicker />
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary">查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
产品报表
</Card>
</PageContainer>
)
}

+ 16
- 0
src/pages/order/order-product-report/index.less View File

@@ -0,0 +1,16 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-card {
}

+ 245
- 0
src/pages/order/order-report-detail/index.jsx View File

@@ -0,0 +1,245 @@
import React, { useState, useEffect } from 'react';
import { Button, Card, Image, Timeline, message, Tag } from 'antd';
import styles from './index.less';
import { ExclamationCircleOutlined, UserOutlined, RedEnvelopeOutlined, WhatsAppOutlined } from '@ant-design/icons';
import { history } from 'umi';
import orderReportAPI from "./service";
import { PageContainer } from '@ant-design/pro-layout';

const orderNumber = history.location.query.orderNumber;

/**
* 订单详情
* @returns
*/
export default function Index() {

const [orderInfo, setOrderInfo] = useState({
memberInfo: {}, //用户信息
goodsInfo: [], //商品信息
informationRefundInfo: [], //售后信息
discountInfo: [] //优惠信息
});

const [orderStatus, setOrderStatus] = useState({
isTrue: true
});

const onQueryOrderDetail = async () => {
if (orderNumber) {
const response = await orderReportAPI.getOrderReporByNumber(orderNumber);
if (response.statusCode === 200) {
setOrderInfo(response.data);
orderIsNormal(response.data);
} else {
message.error(response.errors || '订单详情获取失败');
}
}
}

const orderIsNormal = (order) => {
let isNormal = {
isTrue: true,
msg: '正常订单'
};
if (!(order.payMoneyEx === '正常')) {
isNormal.isTrue = false;
isNormal.msg = '订单支付金额异常';
}
else if (!(order.payOrderTimeEx === '正常')) {
isNormal.isTrue = false;
isNormal.msg = '订单支付时间异常';
}
else if (!(order.payOrderEx === '正常')) {
isNormal.isTrue = false;
isNormal.msg = '订单支付异常';
}
else if (!(order.refundMoneyEx === '正常' || order.refundMoneyEx === '' || order.refundMoneyEx === null)) {
isNormal.isTrue = false;
isNormal.msg = '订单退款金额异常';
}
else if (!(order.refundOrderTimeEx === '正常' || order.refundOrderTimeEx === '' || order.refundOrderTimeEx === null)) {
isNormal.isTrue = false;
isNormal.msg = '订单退款时间异常';
}
else if (!(order.refundOrderEx === '正常' || order.refundOrderEx === '' || order.refundOrderEx === null)) {
isNormal.isTrue = false;
isNormal.msg = '订单退款异常';
}
console.log('isNormal', isNormal);
setOrderStatus(isNormal);
}

useEffect(() => {
onQueryOrderDetail();
}, []);

return (
<PageContainer>
<div className={styles['order-detail-container']}>
{/* 用户基本信息 */}
<div className={styles['order-head-card']}>
<div className={styles['order-head']}>
<div className={styles['order-number']}>
#{orderInfo.sortId}
</div>
<div className={styles['order-cook-state']}>
{
orderStatus.isTrue ? <div className={styles['order-status-true']}>正常订单</div> : <div className={styles['order-status-false']}>{orderStatus.msg}</div>
}
</div>
<div className={styles['order-cook-date']}>
{orderInfo.createdAt}
</div>
</div>
<div className={styles['member-info']}>
<div className={styles['member-info-img']}>
<Image
width={100}
height={100}
src={orderInfo.memberInfo.headUrl}
fallback="data:image/png;base64,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"
/>
</div>
<div className={styles['member-info-row']}>
<div className={styles['member-info-head']}>
{orderInfo.memberInfo.memberName || '系统用户'}
</div>
<div className={styles['member-info-label']}>
<span>#门店新客</span>
<span>#门店会员</span>
</div>
</div>
</div>
<div className={styles['member-info-privacy']}>
<ExclamationCircleOutlined />
<span>为保证服务体验,您在拨打或接听隐私号电话时,可能被录音</span>
</div>
<div className={styles['member-info-item']}>
<span className={styles['member-info-prefix']}>
顾客电话
</span>
<span className={styles['member-info-suffix']}>
{orderInfo.memberInfo.phone || '暂无电话'}
</span>
</div>
<div className={styles['prepare-meals']}>
<div className={styles['prepare-meals-title']}>
优惠信息
</div>
<div className={styles['prepare-meals-status']}>
{
orderInfo.discountInfo?.map(discountItem => {
return (
<div>
{discountItem.type === 1 ? <Tag icon={<UserOutlined />} color="#f50">{discountItem.name}:{discountItem.value}</Tag> : null}
{discountItem.type === 2 ? <Tag icon={<RedEnvelopeOutlined />} color="#87d068">{discountItem.name}:{discountItem.value}</Tag> : null}
{discountItem.type === 3 ? <Tag icon={<WhatsAppOutlined />} color="#108ee9">{discountItem.name}:{discountItem.value}</Tag> : null}
</div>
)
})
}

</div>
</div>
</div>
{/* 订单数据 */}
<div className={styles['order-base-card']}>
<div className={styles['goods-title']}>
订单信息
</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>订单编号</div>
<div className={styles['goods-calc-sufix']}>{orderInfo.orderNumber}</div>
</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>支付方式</div>
<div className={styles['goods-calc-sufix']}>微信支付</div>
</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>下单时间</div>
<div className={styles['goods-calc-sufix']}>{orderInfo.createdAt}</div>
</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>就餐人数</div>
<div className={styles['goods-calc-sufix']}>商家按餐量提供</div>
</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>桌台号码</div>
<div className={styles['goods-calc-sufix']}>0</div>
</div>
</div>
{/* 商品信息 */}
<div className={styles['goods-card']}>
<div className={styles['goods-title']}>
商品信息
</div>
<div className={styles['goods-list']}>
{
orderInfo.goodsInfo.map(item => {
return (
<div className={styles['goods-item']} key={item.goodsId}>
<div className={styles['goods-item-img']}>
<Image
width={80}
height={80}
src={item.goodsImg}
fallback="data:image/png;base64,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"
/>
</div>
<div className={styles['goods-item-name']}>
{item.goodsName || '暂无名称'}
</div>
<div className={styles['goods-item-price']}>
¥{item.memberPrice}
</div>
<div className={styles['goods-item-count']}>
x{item.count}份
</div>
</div>
)
})
}
</div>
{/* 小计 */}
<div className={styles['goods-calc']}>
<div className={styles['goods-calc-title']}>小计</div>
<div className={styles['goods-calc-item']}>
<div className={styles['goods-calc-prefix']}>优惠金额</div>
<div className={styles['goods-calc-sufix']}>¥{orderInfo.discountMoney}</div>
</div>
<div className={`${styles['goods-calc-item']} ${styles['goods-calc-pay']}`}>
<div className={styles['goods-calc-prefix']}>支付金额</div>
<div className={styles['goods-calc-sufix']}>¥{orderInfo.orderRealMoney}</div>
</div>
</div>
</div>
{/* 售后信息 */}
<div className={styles['sale-after-card']}>
<div className={styles['goods-title']}>
售后信息
</div>
<Timeline>
{
orderInfo.informationRefundInfo.length > 0 ?
orderInfo.informationRefundInfo.map(item => {
return (
<Timeline.Item color="green" key={item.id}>
<div className={styles['sale-after-line']}>
{item.createdAt} {item.refundDesc}
</div>
</Timeline.Item>
)
})
:
<Timeline.Item color="green">
暂无售后信息
</Timeline.Item>
}
</Timeline>
</div>
</div>
</PageContainer>

)
}

+ 201
- 0
src/pages/order/order-report-detail/index.less View File

@@ -0,0 +1,201 @@
.order-detail-container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}

.order-head {
display: flex;
align-items: center;
position: relative;
height: 50px;
margin-bottom: 10px;
}

.order-head::before {
position: absolute;
content: '';
width: 0;
height: 0;
top: 50%;
transform: translateY(-50%);
left: 50px;
border-top: 20px solid transparent;
border-bottom: 20px solid transparent;
border-left: 20px solid rgb(88, 91, 110);
}

.order-number {
display: flex;
align-items: center;
justify-content: center;
position: absolute;
width: 70px;
height: 40px;
left: -20px;
top: 50%;
transform: translateY(-50%);
background-color: rgb(88, 91, 110);
color: #FFF;
font-size: 26px;
}

.order-status-true {
font-size: 14px;
padding: 5px 20px;
background-color: #2db7f5;
color: #FFF;
}

.order-status-false {
font-size: 14px;
padding: 5px 20px;
background-color: #F84352;
color: #FFF;
}

.order-cook-state {
font-size: 22px;
margin-left: 100px;
margin-right: 20px;
}

.order-cook-date {
font-size: 16px;
color: rgb(246, 152, 38);
}

// 用户信息
.member-info {
display: flex;
}

.member-info-img {
margin-right: 20px;
}

.member-info-row {
display: flex;
flex-direction: column;
justify-content: center;
}

.member-info-head {
font-size: 22px;
margin-right: 10px;
}

.member-info-label {
font-size: 16px;
color: #999;
}

.member-info-label span {
margin-right: 10px;
}

.member-info-privacy {
margin: 10px 0;
font-size: 16px;
color: rgb(88, 91, 110);
}

.member-info-privacy span {
margin-left: 10px;
}

.member-info-prefix {
font-size: 16px;
color: rgb(88, 91, 110);
margin-right: 10px;
}

.member-info-suffix {
font-size: 16px;
color: #222;
}

// 备餐状态
.prepare-meals {
margin-top: 20px;
font-size: 16px;
color: #222;
}

.prepare-meals-title {
font-weight: 700;
}



// 商品信息
.goods-card {
// margin-top: 20px;
}

.goods-title {
font-size: 22px;
font-weight: 700;
margin-bottom: 10px;
}

.goods-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
font-size: 16px;
color: #222;
}

// 小计
.goods-calc {
padding-top: 10px;
border-top: 1px dashed #dedede;
font-size: 16px;
}

.goods-calc-title {
font-size: 16px;
font-weight: 700;
}

.goods-calc-item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 16px;
}

.goods-calc-pay .goods-calc-sufix {
color: #F84352;
}

// 订单信息
.sale-after-line {
font-size: 16px;
}

@media screen and (min-width: 1200px) {
.order-head-card,
.goods-card,
.order-base-card,
.sale-after-card {
padding: 20px;
background-color: #FFF;
width: 49.5%;
box-sizing: border-box;
}
}

@media screen and (max-width: 1200px) {
.order-head-card,
.goods-card,
.order-base-card,
.sale-after-card {
padding: 20px;
background-color: #FFF;
width: 100%;
box-sizing: border-box;
}
}

+ 9
- 0
src/pages/order/order-report-detail/service.js View File

@@ -0,0 +1,9 @@
import { request } from 'umi';

export default {
getOrderReporByNumber(orderNumber) {
return request(`/kitchen/api/report-statistics/order-info-report/${orderNumber}`, {
method: 'GET',
});
}
};

+ 299
- 0
src/pages/order/order-report/index.jsx View File

@@ -0,0 +1,299 @@
import React, { useState, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker, Table, message, Pagination, Space, Tag, Spin, TreeSelect } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
import orderReportAPI from "./service";
import moment from 'moment';
import { history } from "umi";
import { CheckCircleOutlined, CloseCircleOutlined, DownloadOutlined } from '@ant-design/icons';
import ExportJsonExcel from "js-export-excel"

const columns = [
{
title: '交易单号',
dataIndex: 'transactionId',
key: 'transactionId',
},
{
title: '店铺名称',
dataIndex: 'shopName',
key: 'shopName',
},
{
title: '原价(¥)',
dataIndex: 'orderOriginalMoney',
key: 'orderOriginalMoney',
},
{
title: '优惠(¥)',
dataIndex: 'discountMoney',
key: 'discountMoney',
},
{
title: '实际支付(¥)',
dataIndex: 'orderRealMoney',
key: 'orderRealMoney',
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
},
{
title: '订单状态',
key: 'orderStatus',
render: (record) => {
let isNormal = true;
if (!(record.payMoneyEx === '正常' &&
record.payOrderTimeEx === '正常' &&
record.payOrderEx === '正常')
) {
isNormal = false;
}
if (!(record.refundMoneyEx === '正常' || record.refundMoneyEx === '' || record.refundMoneyEx === null)) {
isNormal = false;
}
if (!(record.refundOrderTimeEx === '正常' || record.refundOrderTimeEx === '' || record.refundOrderTimeEx === null)) {
isNormal = false;
}
if (!(record.refundOrderEx === '正常' || record.refundOrderEx === '' || record.refundOrderEx === null)) {
isNormal = false;
}
return (
<>
{
isNormal ? <Tag icon={<CheckCircleOutlined />} color="success">
正常
</Tag>
:
<Tag icon={<CloseCircleOutlined />} color="error">
异常
</Tag>
}
</>
)
},
},
{
title: '售后信息',
key: 'orderStatus',
render: (record) => {
return (
<>
{
record.isRefund ? <Tag color="error">
</Tag>
:
<Tag color="success">
</Tag>
}
</>
)
},
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space size="middle">
<a onClick={() => {
history.push({
pathname: '/order/order-report/order-report-detail',
query: {
orderNumber: record.orderNumber
}
});
}}>详情</a>
</Space>
),
},
];

const LoadingCard = () => {
return (
<div className={styles['loading-card']}>
<Spin size="large" />
</div>
)
}

/**
* 订单报表
* @returns
*/
export default function Index() {

//订单报表列表
const [orderReportList, setOrderReportList] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [showLoading, setShowLoading] = useState(false);
const [currentOrg, setCurrentOrg] = useState('');
const [orgTree, setOrgTree] = useState([]);


const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]); //日期选择器

useEffect(() => {
onQueryOrderReportList();
}, [current, pageSize]);


useEffect(() => {
onGetOrgTree();
}, []);


//获取订单列表
const onQueryOrderReportList = async () => {
const jsonData = {
startTime: timeRange[0],
endTime: timeRange[1],
current,
pageSize
}
currentOrg.key && (jsonData.shopId = currentOrg.key);
setShowLoading(true);
const response = await orderReportAPI.getOrderReportList(jsonData);
setShowLoading(false);
if (response.statusCode === 200) {
setOrderReportList(response.data.data);
setTotal(response.data.total)
} else {
message.error(response.errors || '获取订单报表列表失败');
}
}

//页码变化
const onPageChange = (current, pageSize) => {
setCurrent(current);
setPageSize(pageSize);
}

//获取组织树
const onGetOrgTree = async () => {
setShowLoading(true);
const response = await orderReportAPI.getOrgTree();
setShowLoading(false);
if (response.statusCode === 200) {
const originTree = response.data;
onSetOrgTreeStatus(originTree);
setOrgTree(originTree);
} else {
message.error(response.errors || '获取组织树出错');
}
}

//设置组织树不可选择状态
const onSetOrgTreeStatus = (originTree) => {
originTree.forEach(treeItem => {
if (treeItem.children && treeItem.children.length > 0) {
onSetOrgTreeStatus(treeItem.children);
} else {
if (treeItem.type === 2 || treeItem.type === 3) {
treeItem.disabled = false;
} else {
treeItem.disabled = true;
}
}
});
}

//下载Excel
const onDownloadExcel = async () => {
const jsonData = {
startTime: timeRange[0],
endTime: timeRange[1],
}
currentOrg.key && (jsonData.shopId = currentOrg.key);
setShowLoading(true);
const response = await orderReportAPI.orderReportExport(jsonData);
setShowLoading(false);
if (response.statusCode === 200) {
let option = {}; //option代表的就是excel文件
const date = new Date();
const excelName = "订单报表-" + date.toLocaleString().replaceAll('\/', '-');
option.fileName = excelName; //excel文件名称
const sheetHeader = [];
Object.keys(response.data[0]).forEach(item => {
sheetHeader.push(item);
});
option.datas = [
{
sheetData: response.data, //excel文件中的数据源
sheetName: excelName, //excel文件中sheet页名称
sheetFilter: sheetHeader, //excel文件中需显示的列数据
sheetHeader: ['ID', '取餐号', '用户Id', '优惠合集', '订单号(本系统)',
'交易号', '店铺ID', '店铺名字', '商品总价', '优惠金额', '实付金额',
'创建时间', '支付金额异常',
'订单支付时间异常', '订单异常', '售后金额异常', '售后时间异常', '售后订单异常'
] //excel文件中每列的表头名称
}
]
let toExcel = new ExportJsonExcel(option); //生成excel文件
toExcel.saveExcel();
} else {
message.error(response.errors || '导出失败');
}
}

return (
<PageContainer>
{showLoading ? <LoadingCard></LoadingCard> : null}
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<div className={styles['data-search-left']}>
<RangePicker className={styles['my-range-picker']} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
]
setTimeRange(tempDate);
}} />
<TreeSelect
style={{
width: '300px',
marginLeft: '50px'
}}
dropdownStyle={{
maxHeight: 400,
overflow: 'auto',
}}
treeData={orgTree}
onSelect={(value, node) => {
console.log('node>>>', node);
if (node.type === 2 || node === 3) {
setCurrentOrg(node);
} else {
setCurrentOrg("");
}
}}
placeholder="请选择组织架构"
treeDefaultExpandAll
/>
</div>
<div className={styles['data-search-btns']}>
<Button type="primary" icon={<DownloadOutlined />} size='middle' onClick={onDownloadExcel}>
下载
</Button>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary" onClick={onQueryOrderReportList}>查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
<Table dataSource={orderReportList} columns={columns} pagination={false} />
<div className={styles['table-page']}>
<Pagination current={current} pageSize={pageSize} total={total} onChange={onPageChange} />
</div>
</Card>
</PageContainer>
)
}

+ 36
- 0
src/pages/order/order-report/index.less View File

@@ -0,0 +1,36 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-page {
display: flex;
justify-content: flex-end;
align-items: center;
height: 50px;
}

// 加载中
.loading-card {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}

+ 25
- 0
src/pages/order/order-report/service.js View File

@@ -0,0 +1,25 @@
import { request } from 'umi';

export default {
getOrderReportList(data) {
return request(`/kitchen/api/report-statistics/order-report`, {
method: 'Post',
data: data,
});
},

//获取组织架构
getOrgTree() {
return request(`/kitchen/api/report-statistics/org-tree`, {
method: 'GET',
});
},

//下载报表
orderReportExport(data) {
return request(`/kitchen/api/report-statistics/order-report-export`, {
method: 'Post',
data: data,
});
}
};

+ 28
- 0
src/pages/order/order-revenue-statement/index.jsx View File

@@ -0,0 +1,28 @@
import React from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
/**
* 营收报表
* @returns
*/
export default function Index() {

return (
<PageContainer>
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<RangePicker />
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary">查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
营收报表
</Card>
</PageContainer>
)
}

+ 16
- 0
src/pages/order/order-revenue-statement/index.less View File

@@ -0,0 +1,16 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-card {
}

+ 229
- 0
src/pages/order/sales-gross-profit/index.jsx View File

@@ -0,0 +1,229 @@
import React, { useState, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, Card, DatePicker, Table, Pagination, Spin, Select, message } from 'antd';
import styles from './index.less';
const { RangePicker } = DatePicker;
import costSalesAPI from "./service";
import moment from 'moment';

const columns = [
{
title: '门店名称',
dataIndex: 'storeId',
key: 'storeId',
},
{
title: '商品名称',
dataIndex: 'goodsId',
key: 'goodsId',
},
{
title: '毛利率',
dataIndex: 'marginRatio',
key: 'marginRatio',
}
];

const LoadingCard = () => {
return (
<div className={styles['loading-card']}>
<Spin size="large" />
</div>
)
}

/**
* 销售毛利分析
* @returns
*/
export default function Index() {

//订单报表列表
const [costSalesData, setCostSalesData] = useState([]);
const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [showLoading, setShowLoading] = useState(false);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now() - 24 * 60 * 60 * 1000 * 7)).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]); //日期选择器
//门店
const [storeIdArray, setStoreIdArray] = useState([]);
const [storeSelect, setStoreSelect] = useState([]);
//商品
const [goodsIdArray, setGoodsIdArray] = useState([]);
const [goodsIdSelect, setGoodsIdSelect] = useState([]);
//商品类型
const [goodsTypeArray, setGoodsTypeArray] = useState([]);
const [goodsTypeSelect, setGoodsTypeSelect] = useState([]);

//页码变化
const onPageChange = (current, pageSize) => {
setCurrent(current);
setPageSize(pageSize);
}

//查询
const onQueryReportSalescost = async () => {
const jsonData = {
"storeId": storeIdArray,
"goodsId": goodsIdArray,
"goodsTypeId": goodsTypeArray,
"begintime": timeRange[0],
"endtime": timeRange[1],
current,
pageSize
}
setShowLoading(true);
const response = await costSalesAPI.getReportSalesmargin({});
setShowLoading(false);
if (response.statusCode === 200) {
setCostSalesData(response.data.data);
} else {
message.error(response.errors || '获取销售成本失败');
}
}

/**
* 查询店铺列表
*/
const onQueryStoreList = async () => {
setShowLoading(true);
const response = await costSalesAPI.gettree({});
setShowLoading(false);
if (response.statusCode === 200) {
const storeList = [];
response.data.forEach(item => {
if (item.type === 2 || item.type === 3) {
storeList.push(item)
}
})
setStoreSelect(storeList);
} else {
message.error('查询店铺列表失败');
}
}

//查询商品列表
const onQueryGoodsList = async () => {
setShowLoading(true);
const response = await costSalesAPI.goodsList({});
setShowLoading(false);
if (response.statusCode === 200) {
setGoodsIdSelect(response.data);
} else {
message.error('查询商品列表失败');
}
}

//查询商品分类类型列表
const onQueryGoodsType = async () => {
setShowLoading(true);
const response = await costSalesAPI.goodsTypeList({});
setShowLoading(false);
if (response.statusCode === 200) {
setGoodsTypeSelect(response.data);
} else {
message.error('查询商品分类列表失败');
}
}

useEffect(() => {
onQueryReportSalescost();
onQueryStoreList();
onQueryGoodsList();
onQueryGoodsType();
}, []);
return (
<PageContainer>
{showLoading ? <LoadingCard></LoadingCard> : null}
<Card className={styles['data-search-card']}>
<div className={styles['data-search-box']}>
<div className={styles['data-search-left']}>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择门店"
onChange={(values) => setStoreIdArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
storeSelect.map( (item) => {
return (
<Option key={item.key}>{item.title}</Option>
)
})
}
</Select>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择商品"
onChange={(values) => setGoodsIdArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
goodsIdSelect.map( (item, index) => {
return (
<Option key={item.id}>{item.name}</Option>
)
})
}
</Select>
<Select
showSearch
mode="tags"
size="middle"
allowClear
placeholder="请选择商品类别"
onChange={(values) => setGoodsTypeArray(values)}
filterOption={(input, option) => option.children.toLowerCase().includes(input.toLowerCase())}
style={{
width: '300px',
marginRight: '50px'
}}
>
{
goodsTypeSelect.map( (item) => {
return (
<Option key={item.id}>{item.name}</Option>
)
})
}
</Select>
<RangePicker size='middle' className={styles['my-range-picker']} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD 23:59:59')),
]
setTimeRange(tempDate);
}} />
</div>
<div className={styles['data-search-btns']}>
<Button className={styles['search-btn-item']}>重置</Button>
<Button className={styles['search-btn-item']} type="primary" onClick={onQueryReportSalescost}>查询</Button>
</div>
</div>
</Card>
<Card className={styles['table-card']}>
<Table dataSource={costSalesData} columns={columns} pagination={false} />
<div className={styles['table-page']}>
<Pagination current={current} pageSize={pageSize} total={total} onChange={onPageChange} />
</div>
</Card>
</PageContainer>
)
}

+ 36
- 0
src/pages/order/sales-gross-profit/index.less View File

@@ -0,0 +1,36 @@
.data-search-card {
margin-bottom: 20px;
}

.data-search-box {
display: flex;
align-items: center;
justify-content: space-between;
}

.search-btn-item {
margin-left: 20px;
}

.table-page {
display: flex;
justify-content: flex-end;
align-items: center;
height: 50px;
}

// 加载中
.loading-card {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.5);
}

+ 39
- 0
src/pages/order/sales-gross-profit/service.js View File

@@ -0,0 +1,39 @@
import { request } from 'umi';

export default {
getReportSalesmargin(data) {
return request(`/kitchen/api/report/salesmargin`, {
method: 'Post',
data: data,
});
},
/**
* 查询商品信息
*/
goodsList(data) {
return request(`/kitchen/api/goodes/list`, {
method: 'Post',
data: data,
});
},
/**
* 查询商品类型
*/
goodsTypeList(data) {
return request(`/kitchen/api/goodstype/list`, {
method: 'Post',
data: data,
});
},

/**
* 查询门店列表
*/
gettree(params) {
return request('​/kitchen/api​/sysOrg​/tree', {
data: {
...params,
},
});
}
};

Loading…
Cancel
Save