mall数据库文件

This commit is contained in:
comlibmb
2026-01-30 16:11:23 +08:00
parent b53d2376ff
commit cfec4a16c0
71 changed files with 11786 additions and 1009 deletions

View File

@@ -105,10 +105,12 @@
</template>
<script lang="uts">
import supa, { ensureSupabaseReady } from '@/components/supadb/aksupainstance.uts'
import AnalyticsSidebarMenu from '@/components/analytics/AnalyticsSidebarMenu.uvue'
import AnalyticsTopBar from '@/components/analytics/AnalyticsTopBar.uvue'
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
import { fetchDataDetailReportInfo, fetchDataDetailRows, fetchDataDetailDrillItems } from '@/services/analytics/dataDetailService.uts'
import { mapAnalyticsError } from '@/services/analytics/errorMapper.uts'
import { rpcOrEmptyArray } from '@/services/analytics/rpc.uts'
type TableColumn = { key: string; label: string; type: string; sortable: boolean }
type DrillDownItem = { id: string; label: string; value: string; type: string }
@@ -125,9 +127,10 @@ export default {
showMoreMenu: false,
timeRangeText: '最近7天',
dimensionText: '全部',
compareMode: false,
compareMode: true,
sortKey: '',
sortOrder: 'asc',
reportId: '',
tableColumns: [
{ key: 'date', label: '日期', type: 'date', sortable: true },
@@ -146,9 +149,10 @@ export default {
onLoad(options: any) {
this.currentPath = '/pages/mall/analytics/data-detail'
// 接收参数:dataType, timeRange, dimension
if (options.dataType) {
// 根据数据类型加载不同的数据
// 接收参数:reportId / id优先报表ID以及可选的数据类型
const rid = (options.reportId || options.id) as string
if (rid) {
this.reportId = rid
}
this.updateTime()
this.loadDetailData()
@@ -161,104 +165,174 @@ export default {
methods: {
async loadDetailData() {
try {
const now = new Date()
const end = new Date(now.getTime())
const start = new Date(now.getTime())
// 如果带有报表 ID则优先使用 DATA_DETAIL_RPCS 基于 analytics_* 表加载
if (this.reportId && this.reportId.length > 0) {
// 1) 报表基础信息(可选:同步时间范围/标题)
const info = await fetchDataDetailReportInfo(this.reportId)
if (info != null) {
const period = info.period
if (period === '7d') this.timeRangeText = '最近7天'
else if (period === '30d') this.timeRangeText = '最近30天'
else if (period === '90d') this.timeRangeText = '最近90天'
}
if (this.timeRangeText === '最近7天') {
start.setDate(start.getDate() - 7)
} else if (this.timeRangeText === '最近30天') {
start.setDate(start.getDate() - 30)
} else if (this.timeRangeText === '最近90天') {
start.setDate(start.getDate() - 90)
} else {
// 自定义暂时按最近30天处理
start.setDate(start.getDate() - 30)
}
// 2) 明细行(表格)
const sortBy = this.sortKey.length > 0 ? this.sortKey : 'row_date'
const sortDir = this.sortOrder === 'desc' ? 'desc' : 'asc'
this.tableData = await fetchDataDetailRows(this.reportId, sortBy, sortDir, 500, 0)
const startIso = start.toISOString()
const endIso = end.toISOString()
// 3) 钻取指标KPI 列表)
// 这里仍保留页面侧的格式化逻辑format/label/valueservice 只负责拉数据
const drillAny = await rpcOrEmptyArray('rpc_data_detail_drill_items', {
p_report_id: this.reportId
} as UTSJSONObject)
const drillList: Array<DrillDownItem> = []
for (let i = 0; i < drillAny.length; i++) {
const m = drillAny[i]
const key = m.getString('metric_key') ?? ''
const label = m.getString('metric_label') ?? key
const fmt = m.getString('format') ?? 'number'
const valueNum = m.getNumber('metric_value_num') ?? 0
const vStr = this.formatCellValue(valueNum, fmt === 'currency' ? 'money' : (fmt === 'percent' ? 'percent' : 'number'))
drillList.push({
id: key.length > 0 ? key : 'metric_' + i.toString(),
label: label,
value: vStr,
type: key
} as DrillDownItem)
}
this.drillDownItems = drillList
await ensureSupabaseReady()
// 当前周期明细:复用 rpc_analytics_market_trend_daily按天 GMV / 订单 / 用户)
let currentRows: Array<UTSJSONObject> = []
let compareRows: Array<UTSJSONObject> = []
const curRes = await supa.rpc('rpc_analytics_market_trend_daily', {
p_start: startIso,
p_end: endIso
})
if (curRes.status === 404) {
console.warn('rpc_analytics_market_trend_daily not found, data-detail will be empty')
} else if (curRes.error != null) {
console.error('rpc_analytics_market_trend_daily error:', curRes.error)
} else {
const anyData = curRes.data as any
currentRows = Array.isArray(anyData) ? anyData as Array<UTSJSONObject> : []
}
// 对比周期:与当前周期长度相同的上一段时间
const spanMs = end.getTime() - start.getTime()
const prevEnd = new Date(start.getTime())
const prevStart = new Date(start.getTime() - spanMs)
const prevStartIso = prevStart.toISOString()
const prevEndIso = prevEnd.toISOString()
const prevRes = await supa.rpc('rpc_analytics_market_trend_daily', {
p_start: prevStartIso,
p_end: prevEndIso
})
if (prevRes.status === 404) {
console.warn('rpc_analytics_market_trend_daily not found for compare period')
} else if (prevRes.error != null) {
console.error('rpc_analytics_market_trend_daily (compare) error:', prevRes.error)
} else {
const anyPrev = prevRes.data as any
compareRows = Array.isArray(anyPrev) ? anyPrev as Array<UTSJSONObject> : []
}
// 映射到表格数据
const table: Array<any> = []
for (let i = 0; i < currentRows.length; i++) {
const r = currentRows[i]
const dayStr = r.getString('day') ?? ''
table.push({
id: dayStr + '_' + i.toString(),
date: dayStr,
gmv: r.getNumber('gmv') ?? 0,
orders: r.getNumber('orders') ?? 0,
users: r.getNumber('users') ?? 0
// 4) GMV 对比曲线
const cmpRes: any = await supa.rpc('rpc_data_detail_compare_gmv', {
p_report_id: this.reportId
} as any)
}
this.tableData = table
let cmpRows: Array<UTSJSONObject> = []
if (cmpRes.error != null) {
console.error('rpc_data_detail_compare_gmv error:', cmpRes.error)
} else {
const anyCmp = cmpRes.data as any
if (Array.isArray(anyCmp)) {
cmpRows = anyCmp as Array<UTSJSONObject>
}
}
// 简单生成钻取卡片:总 GMV / 总订单 / 总用户
let totalGmv = 0
let totalOrders = 0
let totalUsers = 0
for (let i = 0; i < table.length; i++) {
const row = table[i]
totalGmv += row.gmv as number
totalOrders += row.orders as number
totalUsers += row.users as number
}
this.drillDownItems = [
{ id: 'gmv_total', label: '当前周期 GMV 总计', value: this.formatCellValue(totalGmv, 'money'), type: 'gmv' } as DrillDownItem,
{ id: 'orders_total', label: '当前周期订单总数', value: this.formatCellValue(totalOrders, 'number'), type: 'orders' } as DrillDownItem,
{ id: 'users_total', label: '当前周期下单用户数', value: this.formatCellValue(totalUsers, 'number'), type: 'users' } as DrillDownItem
]
const curDays: string[] = []
const curGmv: number[] = []
const prevGmv: number[] = []
for (let i = 0; i < cmpRows.length; i++) {
const r = cmpRows[i]
const dayStr = r.getString('day') ?? ''
curDays.push(dayStr.length >= 10 ? dayStr.substring(5, 10) : dayStr)
curGmv.push(r.getNumber('gmv_current') ?? 0)
prevGmv.push(r.getNumber('gmv_previous') ?? 0)
}
this.compareChartOption = {
tooltip: { trigger: 'axis' },
legend: { data: ['当前周期 GMV', '对比周期 GMV'], top: 'bottom' },
grid: { left: 50, right: 20, top: 30, bottom: 60 },
xAxis: { type: 'category', data: curDays },
yAxis: { type: 'value', name: 'GMV' },
series: [
{ name: '当前周期 GMV', type: 'line', smooth: true, data: curGmv },
{ name: '对比周期 GMV', type: 'line', smooth: true, data: prevGmv }
]
}
} else {
// 兼容旧逻辑无报表ID时直接按时间范围调用市场趋势 RPC
const now = new Date()
const end = new Date(now.getTime())
const start = new Date(now.getTime())
;(this as any)._currentRows = currentRows
;(this as any)._compareRows = compareRows
if (this.timeRangeText === '最近7天') {
start.setDate(start.getDate() - 7)
} else if (this.timeRangeText === '最近30天') {
start.setDate(start.getDate() - 30)
} else if (this.timeRangeText === '最近90天') {
start.setDate(start.getDate() - 90)
} else {
start.setDate(start.getDate() - 30)
}
const startIso = start.toISOString()
const endIso = end.toISOString()
let currentRows: Array<UTSJSONObject> = []
let compareRows: Array<UTSJSONObject> = []
const curRes = await supa.rpc('rpc_analytics_market_trend_daily', {
p_start: startIso,
p_end: endIso
} as any)
if (curRes.status === 404) {
console.warn('rpc_analytics_market_trend_daily not found, data-detail will be empty')
} else if (curRes.error != null) {
console.error('rpc_analytics_market_trend_daily error:', curRes.error)
} else {
const anyData = curRes.data as any
currentRows = Array.isArray(anyData) ? anyData as Array<UTSJSONObject> : []
}
const spanMs = end.getTime() - start.getTime()
const prevEnd = new Date(start.getTime())
const prevStart = new Date(start.getTime() - spanMs)
const prevStartIso = prevStart.toISOString()
const prevEndIso = prevEnd.toISOString()
const prevRes = await supa.rpc('rpc_analytics_market_trend_daily', {
p_start: prevStartIso,
p_end: prevEndIso
} as any)
if (prevRes.status === 404) {
console.warn('rpc_analytics_market_trend_daily not found for compare period')
} else if (prevRes.error != null) {
console.error('rpc_analytics_market_trend_daily (compare) error:', prevRes.error)
} else {
const anyPrev = prevRes.data as any
compareRows = Array.isArray(anyPrev) ? anyPrev as Array<UTSJSONObject> : []
}
const table: Array<any> = []
for (let i = 0; i < currentRows.length; i++) {
const r = currentRows[i]
const dayStr = r.getString('day') ?? ''
table.push({
id: dayStr + '_' + i.toString(),
date: dayStr,
gmv: r.getNumber('gmv') ?? 0,
orders: r.getNumber('orders') ?? 0,
users: r.getNumber('users') ?? 0
} as any)
}
this.tableData = table
let totalGmv = 0
let totalOrders = 0
let totalUsers = 0
for (let i = 0; i < table.length; i++) {
const row = table[i]
totalGmv += row.gmv as number
totalOrders += row.orders as number
totalUsers += row.users as number
}
this.drillDownItems = [
{ id: 'gmv_total', label: '当前周期 GMV 总计', value: this.formatCellValue(totalGmv, 'money'), type: 'gmv' } as DrillDownItem,
{ id: 'orders_total', label: '当前周期订单总数', value: this.formatCellValue(totalOrders, 'number'), type: 'orders' } as DrillDownItem,
{ id: 'users_total', label: '当前周期下单用户数', value: this.formatCellValue(totalUsers, 'number'), type: 'users' } as DrillDownItem
]
;(this as any)._currentRows = currentRows
;(this as any)._compareRows = compareRows
this.buildChartOptions()
}
this.updateTime()
this.buildChartOptions()
} catch (e) {
console.error('loadDetailData failed:', e)
this.updateTime()
this.buildChartOptions()
uni.showToast({ title: '详细数据加载失败', icon: 'none' })
this.updateTime()
this.buildChartOptions()
uni.showToast({ title: mapAnalyticsError(e, { fallbackMessage: '详细数据加载失败' }), icon: 'none' })
}
},
@@ -356,8 +430,8 @@ export default {
const curRows = Array.isArray(curAny) ? curAny as Array<UTSJSONObject> : []
const prevRows = Array.isArray(prevAny) ? prevAny as Array<UTSJSONObject> : []
if (!this.compareMode || curRows.length === 0) {
this.compareChartOption = {}
if (curRows.length === 0) {
this.compareChartOption = {}
return
}