运营报表
This commit is contained in:
@@ -7,6 +7,7 @@ export default defineAppConfig({
|
|||||||
'pages/message/index',
|
'pages/message/index',
|
||||||
'pages/profile/index',
|
'pages/profile/index',
|
||||||
'pages/order-list/index',
|
'pages/order-list/index',
|
||||||
|
'pages/report/index',
|
||||||
'pages/bill/index',
|
'pages/bill/index',
|
||||||
'pages/bill-detail/index',
|
'pages/bill-detail/index',
|
||||||
'pages/payment/index',
|
'pages/payment/index',
|
||||||
|
|||||||
@@ -144,6 +144,53 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== 运营报表入口 =====
|
||||||
|
.report-entry {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
border-top: 1rpx solid #f2f3f5;
|
||||||
|
padding-top: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
width: 84rpx;
|
||||||
|
height: 84rpx;
|
||||||
|
border-radius: 24rpx;
|
||||||
|
background: #fff0f0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__info {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #323233;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
margin-top: 6rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__arrow {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #c8c9cc;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ===== 账单入口 =====
|
// ===== 账单入口 =====
|
||||||
.bill-entry {
|
.bill-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -80,6 +80,11 @@ export default function ProfilePage() {
|
|||||||
Taro.navigateTo({ url: `/pages/order-list/index?status=${status ?? 'all'}` })
|
Taro.navigateTo({ url: `/pages/order-list/index?status=${status ?? 'all'}` })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
/** 运营报表入口 → 运营报表页 */
|
||||||
|
const goReport = useCallback(() => {
|
||||||
|
Taro.navigateTo({ url: '/pages/report/index' })
|
||||||
|
}, [])
|
||||||
|
|
||||||
/** 账单入口 → 账单列表页 */
|
/** 账单入口 → 账单列表页 */
|
||||||
const goBill = useCallback(() => {
|
const goBill = useCallback(() => {
|
||||||
Taro.navigateTo({ url: '/pages/bill/index' })
|
Taro.navigateTo({ url: '/pages/bill/index' })
|
||||||
@@ -162,6 +167,17 @@ export default function ProfilePage() {
|
|||||||
</View>
|
</View>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
{/* 运营报表入口 */}
|
||||||
|
<View className='report-entry' onClick={goReport}>
|
||||||
|
<View className='report-entry__icon'>
|
||||||
|
<Icon name='bar-chart-o' size={28} color='#ee0a24' />
|
||||||
|
</View>
|
||||||
|
<View className='report-entry__info'>
|
||||||
|
<Text className='report-entry__title'>运营报表</Text>
|
||||||
|
<Text className='report-entry__desc'>按周/月统计采购金额与单品占比</Text>
|
||||||
|
</View>
|
||||||
|
<Text className='report-entry__arrow'>›</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* ========== 我的账单 ========== */}
|
{/* ========== 我的账单 ========== */}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '运营报表',
|
||||||
|
})
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
.report-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
padding: 20rpx 24rpx 60rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
// ===== 周期切换 =====
|
||||||
|
.period-bar {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.period-chip {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 14rpx 0;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: #fff;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #323233;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background: #ee0a24;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 汇总卡片 =====
|
||||||
|
.report-summary {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
background: linear-gradient(135deg, #ee0a24, #ff4d4f);
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 32rpx 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
font-size: 24rpx;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 56rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__range {
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta {
|
||||||
|
margin-top: 28rpx;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
border-top: 1rpx solid rgba(255, 255, 255, 0.25);
|
||||||
|
padding-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta-value {
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__meta-label {
|
||||||
|
margin-top: 6rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 单品排行 =====
|
||||||
|
.report-list {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 24rpx 28rpx 8rpx;
|
||||||
|
|
||||||
|
&__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__desc {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-item {
|
||||||
|
display: flex;
|
||||||
|
padding: 24rpx 0;
|
||||||
|
border-bottom: 1rpx solid #f2f3f5;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__rank {
|
||||||
|
width: 44rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #f2f3f5;
|
||||||
|
color: #969799;
|
||||||
|
font-size: 24rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
|
||||||
|
&--top {
|
||||||
|
background: #fff0f0;
|
||||||
|
color: #ee0a24;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__main {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-left: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #323233;
|
||||||
|
font-weight: 600;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__amount {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #ee0a24;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__spec {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #969799;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__percent {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #323233;
|
||||||
|
margin-left: 16rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bar {
|
||||||
|
margin-top: 14rpx;
|
||||||
|
height: 8rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: #f2f3f5;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__bar-inner {
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: linear-gradient(90deg, #ff8a8f, #ee0a24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 空态 / 加载中 =====
|
||||||
|
.report-empty {
|
||||||
|
margin-top: 60rpx;
|
||||||
|
|
||||||
|
&__btn {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
padding: 14rpx 48rpx;
|
||||||
|
border-radius: 999rpx;
|
||||||
|
background: #ee0a24;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-loading {
|
||||||
|
padding: 60rpx 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
import { useCallback, useRef, useState } from 'react'
|
||||||
|
import Taro, { useDidShow } from '@tarojs/taro'
|
||||||
|
import { View, Text } from '@tarojs/components'
|
||||||
|
import { Calendar, Empty } from '@antmjs/vantui'
|
||||||
|
import useAuthStore from '@/stores/auth/useAuthStore'
|
||||||
|
import { getPurchaseReportApi } from '@/services/report'
|
||||||
|
import type { PurchaseReport, PurchaseReportParams, ReportPreset } from '@/services/report'
|
||||||
|
import './index.less'
|
||||||
|
|
||||||
|
/** 周期选项 key(custom 为前端伪预设:选中自定义区间后生效) */
|
||||||
|
type PeriodKey = ReportPreset
|
||||||
|
|
||||||
|
/** 周期切换 chips */
|
||||||
|
const PERIOD_TABS: Array<{ key: PeriodKey; label: string }> = [
|
||||||
|
{ key: 'week', label: '本周' },
|
||||||
|
{ key: 'last_week', label: '上周' },
|
||||||
|
{ key: 'month', label: '本月' },
|
||||||
|
{ key: 'last_month', label: '上月' },
|
||||||
|
{ key: 'custom', label: '自定义' },
|
||||||
|
]
|
||||||
|
|
||||||
|
/** 自定义区间可选范围:2020-01-01 ~ 今天(进行中的周期由后端封顶今天) */
|
||||||
|
const MIN_DATE = new Date(2020, 0, 1).getTime()
|
||||||
|
const MAX_DATE = Date.now()
|
||||||
|
|
||||||
|
/** Date → Y-m-d */
|
||||||
|
function formatDate(d: Date): string {
|
||||||
|
const m = `${d.getMonth() + 1}`.padStart(2, '0')
|
||||||
|
const day = `${d.getDate()}`.padStart(2, '0')
|
||||||
|
return `${d.getFullYear()}-${m}-${day}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运营报表页
|
||||||
|
* 按周期(本周/上周/本月/上月/自定义区间)统计门店采购总金额与单品累计金额占比;
|
||||||
|
* 数据为下单快照口径,已取消/已删除订单不计入
|
||||||
|
*/
|
||||||
|
export default function ReportPage() {
|
||||||
|
const token = useAuthStore(s => s.token)
|
||||||
|
|
||||||
|
const [period, setPeriod] = useState<PeriodKey>('month')
|
||||||
|
/** 自定义区间(period=custom 时使用) */
|
||||||
|
const [range, setRange] = useState<{ start: string; end: string } | null>(null)
|
||||||
|
const [report, setReport] = useState<PurchaseReport | null>(null)
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [showCalendar, setShowCalendar] = useState(false)
|
||||||
|
const loadingRef = useRef(false)
|
||||||
|
|
||||||
|
const loggedIn = !!token
|
||||||
|
|
||||||
|
/** 拉取报表 */
|
||||||
|
const load = useCallback(
|
||||||
|
async (params: PurchaseReportParams) => {
|
||||||
|
if (!loggedIn || loadingRef.current) return
|
||||||
|
loadingRef.current = true
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const res = await getPurchaseReportApi(params)
|
||||||
|
setReport(res.data)
|
||||||
|
} catch {
|
||||||
|
// 错误已由 request 层 toast
|
||||||
|
} finally {
|
||||||
|
loadingRef.current = false
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[loggedIn],
|
||||||
|
)
|
||||||
|
|
||||||
|
useDidShow(() => {
|
||||||
|
load(
|
||||||
|
period === 'custom' && range
|
||||||
|
? { start_date: range.start, end_date: range.end }
|
||||||
|
: { preset: period === 'custom' ? 'month' : period },
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 切换周期;自定义打开日历选择区间 */
|
||||||
|
const handlePeriodTap = useCallback(
|
||||||
|
(key: PeriodKey) => {
|
||||||
|
if (key === 'custom') {
|
||||||
|
setShowCalendar(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (key === period) return
|
||||||
|
setPeriod(key)
|
||||||
|
load({ preset: key })
|
||||||
|
},
|
||||||
|
[period, load],
|
||||||
|
)
|
||||||
|
|
||||||
|
/** 日历确认区间 → 自定义区间查询(优先于 preset) */
|
||||||
|
const handleCalendarConfirm = useCallback(
|
||||||
|
(e: { detail: { value: Date | Date[] } }) => {
|
||||||
|
const value = Array.isArray(e.detail.value) ? e.detail.value : [e.detail.value]
|
||||||
|
const [start, end] = value
|
||||||
|
if (!start || !end) return
|
||||||
|
const next = { start: formatDate(start), end: formatDate(end) }
|
||||||
|
setRange(next)
|
||||||
|
setPeriod('custom')
|
||||||
|
setShowCalendar(false)
|
||||||
|
load({ start_date: next.start, end_date: next.end })
|
||||||
|
},
|
||||||
|
[load],
|
||||||
|
)
|
||||||
|
|
||||||
|
const goLogin = useCallback(() => {
|
||||||
|
Taro.navigateTo({ url: '/pages/login/index' })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className='report-page'>
|
||||||
|
{/* ========== 周期切换 ========== */}
|
||||||
|
<View className='period-bar'>
|
||||||
|
{PERIOD_TABS.map(tab => (
|
||||||
|
<View
|
||||||
|
key={tab.key}
|
||||||
|
className={`period-chip ${period === tab.key ? 'active' : ''}`}
|
||||||
|
onClick={() => handlePeriodTap(tab.key)}
|
||||||
|
>
|
||||||
|
<Text>{tab.label}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{!loggedIn ? (
|
||||||
|
<Empty description='登录后查看运营报表' className='report-empty'>
|
||||||
|
<View className='report-empty__btn' onClick={goLogin}>去登录</View>
|
||||||
|
</Empty>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* ========== 汇总卡片 ========== */}
|
||||||
|
{report && (
|
||||||
|
<View className='report-summary'>
|
||||||
|
<Text className='report-summary__label'>采购总金额(元)</Text>
|
||||||
|
<Text className='report-summary__amount'>¥{report.total_amount}</Text>
|
||||||
|
<Text className='report-summary__range'>
|
||||||
|
{report.start_date} ~ {report.end_date}
|
||||||
|
</Text>
|
||||||
|
<View className='report-summary__meta'>
|
||||||
|
<View className='report-summary__meta-item'>
|
||||||
|
<Text className='report-summary__meta-value'>{report.order_count}</Text>
|
||||||
|
<Text className='report-summary__meta-label'>订货单数</Text>
|
||||||
|
</View>
|
||||||
|
<View className='report-summary__meta-item'>
|
||||||
|
<Text className='report-summary__meta-value'>{report.item_count}</Text>
|
||||||
|
<Text className='report-summary__meta-label'>单品数</Text>
|
||||||
|
</View>
|
||||||
|
<View className='report-summary__meta-item'>
|
||||||
|
<Text className='report-summary__meta-value'>{report.total_quantity}</Text>
|
||||||
|
<Text className='report-summary__meta-label'>订货总量</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ========== 单品排行 ========== */}
|
||||||
|
{report && report.items.length > 0 && (
|
||||||
|
<View className='report-list'>
|
||||||
|
<View className='report-list__header'>
|
||||||
|
<Text className='report-list__title'>单品采购排行</Text>
|
||||||
|
<Text className='report-list__desc'>共 {report.item_count} 种,按金额降序</Text>
|
||||||
|
</View>
|
||||||
|
{report.items.map((item, index) => (
|
||||||
|
<View key={item.product_id} className='report-item'>
|
||||||
|
<View className={`report-item__rank ${index < 3 ? 'report-item__rank--top' : ''}`}>
|
||||||
|
{index + 1}
|
||||||
|
</View>
|
||||||
|
<View className='report-item__main'>
|
||||||
|
<View className='report-item__row'>
|
||||||
|
<Text className='report-item__name'>{item.product_name}</Text>
|
||||||
|
<Text className='report-item__amount'>¥{item.amount}</Text>
|
||||||
|
</View>
|
||||||
|
<View className='report-item__row'>
|
||||||
|
<Text className='report-item__spec'>
|
||||||
|
{item.product_spec ? `${item.product_spec} · ` : ''}
|
||||||
|
累计 {item.quantity}{item.unit}
|
||||||
|
{parseFloat(item.weight) > 0 ? ` · 称重 ${item.weight}` : ''}
|
||||||
|
</Text>
|
||||||
|
<Text className='report-item__percent'>{item.percent}%</Text>
|
||||||
|
</View>
|
||||||
|
<View className='report-item__bar'>
|
||||||
|
<View
|
||||||
|
className='report-item__bar-inner'
|
||||||
|
style={{ width: `${Math.min(Math.max(item.percent, 0), 100)}%` }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ========== 空态 / 加载中 ========== */}
|
||||||
|
{(!report || report.items.length === 0) && (
|
||||||
|
loading ? (
|
||||||
|
<View className='report-loading'><Text>加载中...</Text></View>
|
||||||
|
) : (
|
||||||
|
<Empty description='该时间段暂无采购数据' className='report-empty' />
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ========== 自定义区间日历 ========== */}
|
||||||
|
<Calendar
|
||||||
|
show={showCalendar}
|
||||||
|
type='range'
|
||||||
|
allowSameDay
|
||||||
|
firstDayOfWeek={1}
|
||||||
|
minDate={MIN_DATE}
|
||||||
|
maxDate={MAX_DATE}
|
||||||
|
color='#ee0a24'
|
||||||
|
title='选择统计区间'
|
||||||
|
defaultDate={range ? [new Date(range.start).getTime(), new Date(range.end).getTime()] : undefined}
|
||||||
|
onClose={() => setShowCalendar(false)}
|
||||||
|
onConfirm={handleCalendarConfirm}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { get } from '@/utils/request'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计周期预设:
|
||||||
|
* week 本周 / last_week 上周 / month 本月 / last_month 上月;
|
||||||
|
* custom 仅出现在响应中(传 start_date + end_date 自定义区间时生效,优先于 preset)
|
||||||
|
*/
|
||||||
|
export type ReportPreset = 'week' | 'last_week' | 'month' | 'last_month' | 'custom'
|
||||||
|
|
||||||
|
/** 单品累计行(按金额降序) */
|
||||||
|
export interface PurchaseReportItem {
|
||||||
|
product_id: number
|
||||||
|
/** 品名(下单时快照) */
|
||||||
|
product_name: string
|
||||||
|
/** 规格/包规(快照) */
|
||||||
|
product_spec: string
|
||||||
|
/** 计价单位(快照) */
|
||||||
|
unit: string
|
||||||
|
/** 周期内累计订货量 */
|
||||||
|
quantity: number
|
||||||
|
/** 周期内累计重量(3 位小数,未称重为 0.000) */
|
||||||
|
weight: string
|
||||||
|
/** 周期内累计采购金额(元,2 位小数字符串) */
|
||||||
|
amount: string
|
||||||
|
/** 金额占比(%,1 位小数;如 8.8 表示 8.8%) */
|
||||||
|
percent: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 采购运营报表 */
|
||||||
|
export interface PurchaseReport {
|
||||||
|
/** 实际生效的周期预设 */
|
||||||
|
preset: ReportPreset
|
||||||
|
/** 实际统计开始日期(Y-m-d,进行中的周期封顶为今天) */
|
||||||
|
start_date: string
|
||||||
|
/** 实际统计结束日期(Y-m-d) */
|
||||||
|
end_date: string
|
||||||
|
/** 周期内采购总金额(元,2 位小数字符串) */
|
||||||
|
total_amount: string
|
||||||
|
/** 周期内订货总量(各单品数量之和) */
|
||||||
|
total_quantity: number
|
||||||
|
/** 周期内有效订货单数 */
|
||||||
|
order_count: number
|
||||||
|
/** 单品个数(= items 长度) */
|
||||||
|
item_count: number
|
||||||
|
/** 单品累计列表,按金额降序 */
|
||||||
|
items: PurchaseReportItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 报表查询参数:自定义区间(start_date + end_date 需成对)优先于 preset,preset 缺省为 month */
|
||||||
|
export interface PurchaseReportParams {
|
||||||
|
preset?: Exclude<ReportPreset, 'custom'>
|
||||||
|
/** 自定义开始日期(Y-m-d) */
|
||||||
|
start_date?: string
|
||||||
|
/** 自定义结束日期(Y-m-d),不得早于 start_date */
|
||||||
|
end_date?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 采购运营报表:GET /mini/report/purchase(仅当前门店自身数据) */
|
||||||
|
export function getPurchaseReportApi(params: PurchaseReportParams = {}) {
|
||||||
|
return get<PurchaseReport>('/mini/report/purchase', { data: params })
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user