mall数据库文件
This commit is contained in:
@@ -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/value),service 只负责拉数据
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user