数据分析ui补充完善,接入数据库

This commit is contained in:
comlibmb
2026-01-31 21:47:42 +08:00
parent 8f181b2b6a
commit 6716398175
71 changed files with 6501 additions and 10593 deletions

View File

@@ -266,14 +266,7 @@ import supa from '@/components/supadb/aksupainstance.uts'
import AnalyticsSidebarMenu from '@/components/analytics/AnalyticsSidebarMenu.uvue'
import type { UserType } from '@/types/mall-types'
// 报表类型定义
type ReportType = {
id: string
title: string
description: string
status: string
created_at: string
}
import type { RecentReport, OverviewData, ReportCounts, TodayInsights, TrendDatum } from '@/types/analytics/profile.uts'
// 响应式数据
const showSidebarMenu = ref(false)
@@ -293,7 +286,7 @@ const analystInfo = ref({
const workExperience = ref(5)
const expertise = ref('电商数据')
const overviewData = ref({
const overviewData = ref<OverviewData>({
totalSales: '0',
salesGrowth: 0,
totalUsers: '0',
@@ -304,24 +297,24 @@ const overviewData = ref({
conversionGrowth: 0
})
const reportCounts = ref({
const reportCounts = ref<ReportCounts>({
total: 0,
pending: 0,
scheduled: 0,
shared: 0
})
const todayInsights = ref({
const todayInsights = ref<TodayInsights>({
hotProduct: '-',
peakTraffic: '0',
conversionAnomaly: '-',
mobileRatio: 0
})
const recentReports = ref([] as Array<ReportType>)
const recentReports = ref<Array<RecentReport>>([])
const trendPeriod = ref('week')
const trendData = ref([
const trendData = ref<Array<TrendDatum>>([
{ label: '周一', sales: 0, orders: 0 },
{ label: '周二', sales: 0, orders: 0 },
{ label: '周三', sales: 0, orders: 0 },
@@ -457,85 +450,30 @@ async function loadRecentReports() {
async function loadOverview() {
try {
const now = new Date()
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1) // < end
const start = new Date(end.getTime() - 30 * 24 * 60 * 60 * 1000)
const prevEnd = start
const prevStart = new Date(prevEnd.getTime() - 30 * 24 * 60 * 60 * 1000)
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate())
const start = new Date(end.getTime() - 29 * 24 * 60 * 60 * 1000) // Last 30 days
const curRes: any = await supa
.from('orders')
.select('total_amount, user_id, created_at')
.gte('created_at', start.toISOString())
.lt('created_at', end.toISOString())
.eq('status', 2)
const prevRes: any = await supa
.from('orders')
.select('total_amount, user_id, created_at')
.gte('created_at', prevStart.toISOString())
.lt('created_at', prevEnd.toISOString())
.eq('status', 2)
const p = new UTSJSONObject()
p.set('p_start_date', dateISO(start))
p.set('p_end_date', dateISO(end))
const curOrders: Array<any> = Array.isArray(curRes.data) ? (curRes.data as Array<any>) : []
const prevOrders: Array<any> = Array.isArray(prevRes.data) ? (prevRes.data as Array<any>) : []
const [salesKpisRes, userKpisRes] = await Promise.all([
supa.rpc('rpc_analytics_sales_kpis', p),
supa.rpc('rpc_analytics_user_kpis', p)
])
let curSales = 0
let prevSales = 0
const curUsers: Record<string, boolean> = {}
const prevUsers: Record<string, boolean> = {}
for (let i = 0; i < curOrders.length; i++) {
curSales += safeNumber(curOrders[i].total_amount)
const uid = `${curOrders[i].user_id || ''}`
if (uid) curUsers[uid] = true
}
for (let i = 0; i < prevOrders.length; i++) {
prevSales += safeNumber(prevOrders[i].total_amount)
const uid = `${prevOrders[i].user_id || ''}`
if (uid) prevUsers[uid] = true
}
const curOrderCnt = curOrders.length
const prevOrderCnt = prevOrders.length
const curUserCnt = Object.keys(curUsers).length
const prevUserCnt = Object.keys(prevUsers).length
// 转化率:下单用户 / 访问用户(用 user_sessions 近30天会话去重近似
const curSessRes: any = await supa
.from('user_sessions')
.select('user_id, created_at')
.gte('created_at', start.toISOString())
.lt('created_at', end.toISOString())
const prevSessRes: any = await supa
.from('user_sessions')
.select('user_id, created_at')
.gte('created_at', prevStart.toISOString())
.lt('created_at', prevEnd.toISOString())
const curSess: Array<any> = Array.isArray(curSessRes.data) ? (curSessRes.data as Array<any>) : []
const prevSess: Array<any> = Array.isArray(prevSessRes.data) ? (prevSessRes.data as Array<any>) : []
const curVisitUsers: Record<string, boolean> = {}
const prevVisitUsers: Record<string, boolean> = {}
for (let i = 0; i < curSess.length; i++) {
const uid = `${curSess[i].user_id || ''}`
if (uid) curVisitUsers[uid] = true
}
for (let i = 0; i < prevSess.length; i++) {
const uid = `${prevSess[i].user_id || ''}`
if (uid) prevVisitUsers[uid] = true
}
const curVisitCnt = Object.keys(curVisitUsers).length
const prevVisitCnt = Object.keys(prevVisitUsers).length
const curConv = curVisitCnt > 0 ? (curUserCnt / curVisitCnt) * 100 : 0
const prevConv = prevVisitCnt > 0 ? (prevUserCnt / prevVisitCnt) * 100 : 0
const salesKpis = Array.isArray(salesKpisRes.data) && salesKpisRes.data.length > 0 ? salesKpisRes.data[0] : {}
const userKpis = Array.isArray(userKpisRes.data) && userKpisRes.data.length > 0 ? userKpisRes.data[0] : {}
overviewData.value = {
totalSales: fmtMoney(curSales),
salesGrowth: safeNumber(pctGrowth(curSales, prevSales)),
totalUsers: fmtInt(curUserCnt),
userGrowth: safeNumber(pctGrowth(curUserCnt, prevUserCnt)),
totalOrders: fmtInt(curOrderCnt),
orderGrowth: safeNumber(pctGrowth(curOrderCnt, prevOrderCnt)),
conversionRate: safeNumber(curConv),
conversionGrowth: safeNumber(pctGrowth(curConv, prevConv))
totalSales: fmtMoney(safeNumber(salesKpis.gmv)),
salesGrowth: safeNumber(salesKpis.gmv_growth),
totalUsers: fmtInt(safeNumber(userKpis.new_users)), // Use new_users for the period
userGrowth: safeNumber(userKpis.new_user_growth),
totalOrders: fmtInt(safeNumber(salesKpis.orders)),
orderGrowth: safeNumber(salesKpis.order_growth),
conversionRate: safeNumber(salesKpis.conversion_rate),
conversionGrowth: safeNumber(salesKpis.conversion_growth)
}
} catch (e) {
console.error('loadOverview failed', e)
@@ -551,8 +489,7 @@ async function loadTrend() {
const p = new UTSJSONObject()
p.set('p_start_date', dateISO(start))
p.set('p_end_date', dateISO(end))
p.set('p_merchant_id', null)
const res: any = await supa.rpc('rpc_analytics_trend_data', p)
const res: any = await supa.rpc('rpc_analytics_sales_trend', p)
const rows: Array<any> = Array.isArray(res.data) ? (res.data as Array<any>) : []
const weekLabels = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
@@ -571,8 +508,7 @@ async function loadTrend() {
const p = new UTSJSONObject()
p.set('p_start_date', dateISO(start))
p.set('p_end_date', dateISO(new Date(now.getFullYear(), now.getMonth(), now.getDate())))
p.set('p_merchant_id', null)
const res: any = await supa.rpc('rpc_analytics_trend_data', p)
const res: any = await supa.rpc('rpc_analytics_sales_trend', p)
const rows: Array<any> = Array.isArray(res.data) ? (res.data as Array<any>) : []
const buckets: Record<string, { sales: number; orders: number }> = {}
@@ -595,8 +531,7 @@ async function loadTrend() {
const p = new UTSJSONObject()
p.set('p_start_date', dateISO(start))
p.set('p_end_date', dateISO(end))
p.set('p_merchant_id', null)
const res: any = await supa.rpc('rpc_analytics_trend_data', p)
const res: any = await supa.rpc('rpc_analytics_sales_trend', p)
const rows: Array<any> = Array.isArray(res.data) ? (res.data as Array<any>) : []
const buckets: Record<string, { sales: number; orders: number }> = {}
@@ -628,32 +563,23 @@ async function loadTodayInsights() {
p.set('p_start_date', dateISO(today0))
p.set('p_end_date', dateISO(today0))
p.set('p_limit', 1)
p.set('p_merchant_id', null)
const prodRes: any = await supa.rpc('rpc_analytics_top_products', p)
const prodRows: Array<any> = Array.isArray(prodRes.data) ? (prodRes.data as Array<any>) : []
if (prodRows.length > 0) {
todayInsights.value.hotProduct = `${prodRows[0].name}`
}
// 访问量峰值(简化:今日总访问量
// 访问量峰值用今日浏览行为数近似主库口径ml_browse_history
const pvRes: any = await supa
.from('page_views')
.from('ml_browse_history')
.select('id, created_at')
.gte('created_at', today0.toISOString())
.lt('created_at', new Date(today0.getTime() + 24 * 60 * 60 * 1000).toISOString())
const pvRows: Array<any> = Array.isArray(pvRes.data) ? (pvRes.data as Array<any>) : []
todayInsights.value.peakTraffic = fmtInt(pvRows.length)
// 转化异常:取今日 KPI 增长简化负数提示“下降xx%”)
const kpiP = new UTSJSONObject()
kpiP.set('p_start', today0.toISOString())
kpiP.set('p_end', now.toISOString())
const ySame = new Date(now.getTime() - 24 * 60 * 60 * 1000)
const y0 = new Date(ySame.getFullYear(), ySame.getMonth(), ySame.getDate())
kpiP.set('p_compare_start', y0.toISOString())
kpiP.set('p_compare_end', ySame.toISOString())
kpiP.set('p_merchant_id', null)
const kpiRes: any = await supa.rpc('rpc_analytics_realtime_kpis', kpiP)
// 转化异常:使用无参版 rpc_analytics_realtime_kpis
const kpiRes: any = await supa.rpc('rpc_analytics_realtime_kpis', {} as any)
const row = Array.isArray(kpiRes.data) && kpiRes.data.length > 0 ? kpiRes.data[0] : (kpiRes.data || {})
const cg = safeNumber(row.conversion_growth)
todayInsights.value.conversionAnomaly = cg < 0 ? `下降${Math.abs(cg).toFixed(1)}%` : `上升${cg.toFixed(1)}%`