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

This commit is contained in:
comlibmb
2026-01-29 17:30:39 +08:00
parent 3e89513e8b
commit b53d2376ff
13 changed files with 1161 additions and 889 deletions

View File

@@ -94,7 +94,7 @@
</template>
<script lang="uts">
import supa from '@/components/supadb/aksupainstance.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'
@@ -148,9 +148,108 @@ export default {
methods: {
async loadMarketData() {
// TODO: 实现市场数据加载
this.updateTime()
this.buildChartOptions()
try {
const now = new Date()
const start = new Date(now.getTime())
if (this.selectedPeriod === '7d') start.setDate(start.getDate() - 7)
else if (this.selectedPeriod === '30d') start.setDate(start.getDate() - 30)
else if (this.selectedPeriod === '90d') start.setDate(start.getDate() - 90)
else if (this.selectedPeriod === '1y') start.setFullYear(start.getFullYear() - 1)
const startIso = start.toISOString()
const endIso = now.toISOString()
await ensureSupabaseReady()
// 1) 市场整体趋势(按天 GMV / 订单 / 用户)
let trendRows: Array<UTSJSONObject> = []
let categoryRows: Array<UTSJSONObject> = []
let seasonalRows: Array<UTSJSONObject> = []
let priceRows: Array<UTSJSONObject> = []
let competitionRows: Array<UTSJSONObject> = []
const marketRes = await supa.rpc('rpc_analytics_market_trend_daily', {
p_start: startIso,
p_end: endIso
})
if (marketRes.status === 404) {
console.warn('rpc_analytics_market_trend_daily not found, market trend will be empty')
} else if (marketRes.error != null) {
console.error('rpc_analytics_market_trend_daily error:', marketRes.error)
} else {
const anyData = marketRes.data as any
trendRows = Array.isArray(anyData) ? anyData as Array<UTSJSONObject> : []
}
// 2) 行业对比(按分类 GMV
const catRes = await supa.rpc('rpc_analytics_category_sales', {
p_start_date: startIso.substring(0, 10),
p_end_date: endIso.substring(0, 10)
})
if (catRes.status === 404) {
console.warn('rpc_analytics_category_sales not found, industry comparison will be empty')
} else if (catRes.error != null) {
console.error('rpc_analytics_category_sales error:', catRes.error)
} else {
const cAny = catRes.data as any
categoryRows = Array.isArray(cAny) ? cAny as Array<UTSJSONObject> : []
}
// 3) 季节性趋势(按月 GMV
const seaRes = await supa.rpc('rpc_analytics_seasonal_trend', {
p_start_date: startIso.substring(0, 10),
p_end_date: endIso.substring(0, 10)
})
if (seaRes.status === 404) {
console.warn('rpc_analytics_seasonal_trend not found, seasonal trend will be empty')
} else if (seaRes.error != null) {
console.error('rpc_analytics_seasonal_trend error:', seaRes.error)
} else {
const sAny = seaRes.data as any
seasonalRows = Array.isArray(sAny) ? sAny as Array<UTSJSONObject> : []
}
// 4) 价格趋势(按天平均单价)
const priceRes = await supa.rpc('rpc_analytics_price_trend', {
p_start: startIso,
p_end: endIso
})
if (priceRes.status === 404) {
console.warn('rpc_analytics_price_trend not found, price trend will be empty')
} else if (priceRes.error != null) {
console.error('rpc_analytics_price_trend error:', priceRes.error)
} else {
const pAny = priceRes.data as any
priceRows = Array.isArray(pAny) ? pAny as Array<UTSJSONObject> : []
}
// 5) 竞争分析(商家 GMV 榜单)
const compRes = await supa.rpc('rpc_analytics_competition_share', {
p_start_date: startIso.substring(0, 10),
p_end_date: endIso.substring(0, 10)
})
if (compRes.status === 404) {
console.warn('rpc_analytics_competition_share not found, competition analysis will be empty')
} else if (compRes.error != null) {
console.error('rpc_analytics_competition_share error:', compRes.error)
} else {
const cpAny = compRes.data as any
competitionRows = Array.isArray(cpAny) ? cpAny as Array<UTSJSONObject> : []
}
;(this as any)._marketTrendRows = trendRows
;(this as any)._industryRows = categoryRows
;(this as any)._seasonalRows = seasonalRows
;(this as any)._priceRows = priceRows
;(this as any)._competitionRows = competitionRows
this.updateTime()
this.buildChartOptions()
} catch (e) {
console.error('loadMarketData failed:', e)
this.updateTime()
this.buildChartOptions()
uni.showToast({ title: '市场趋势数据加载失败', icon: 'none' })
}
},
selectPeriod(p: string) {
@@ -178,12 +277,162 @@ export default {
},
buildChartOptions() {
// TODO: 构建图表配置
this.marketTrendOption = {}
this.industryCompareOption = {}
this.seasonalTrendOption = {}
this.priceTrendOption = {}
this.competitionOption = {}
const trendAny = (this as any)._marketTrendRows as any
const industryAny = (this as any)._industryRows as any
const seasonalAny = (this as any)._seasonalRows as any
const priceAny = (this as any)._priceRows as any
const compAny = (this as any)._competitionRows as any
const trendRows = Array.isArray(trendAny) ? trendAny as Array<UTSJSONObject> : []
const industryRows = Array.isArray(industryAny) ? industryAny as Array<UTSJSONObject> : []
const seasonalRows = Array.isArray(seasonalAny) ? seasonalAny as Array<UTSJSONObject> : []
const priceRows = Array.isArray(priceAny) ? priceAny as Array<UTSJSONObject> : []
const compRows = Array.isArray(compAny) ? compAny as Array<UTSJSONObject> : []
// 1) 市场整体趋势GMV / 订单数 / 用户数
const mtDays: string[] = []
const mtGmv: number[] = []
const mtOrders: number[] = []
const mtUsers: number[] = []
for (let i = 0; i < trendRows.length; i++) {
const r = trendRows[i]
const dayStr = r.getString('day') ?? ''
mtDays.push(dayStr.length >= 10 ? dayStr.substring(5, 10) : dayStr)
mtGmv.push(r.getNumber('gmv') ?? 0)
mtOrders.push(r.getNumber('orders') ?? 0)
mtUsers.push(r.getNumber('users') ?? 0)
}
this.marketTrendOption = {
tooltip: { trigger: 'axis' },
legend: {
data: ['GMV', '订单数', '用户数'],
top: 'bottom'
},
grid: { left: 50, right: 60, top: 40, bottom: 60 },
xAxis: { type: 'category', data: mtDays },
yAxis: [
{ type: 'value', name: 'GMV', splitLine: { lineStyle: { color: '#e5e7eb' } } },
{ type: 'value', name: '数量', position: 'right', splitLine: { show: false } }
],
series: [
{
name: 'GMV',
type: 'bar',
data: mtGmv,
barMaxWidth: 26,
itemStyle: { color: '#3b82f6' }
},
{
name: '订单数',
type: 'line',
yAxisIndex: 1,
smooth: true,
data: mtOrders
},
{
name: '用户数',
type: 'line',
yAxisIndex: 1,
smooth: true,
data: mtUsers
}
]
}
// 2) 行业对比:分类 GMV
const catNames: string[] = []
const catSales: number[] = []
for (let i = 0; i < industryRows.length; i++) {
const r = industryRows[i]
catNames.push(r.getString('category_name') ?? '未分类')
catSales.push(r.getNumber('total_sales') ?? 0)
}
this.industryCompareOption = {
tooltip: { trigger: 'axis' },
grid: { left: 80, right: 20, top: 30, bottom: 60 },
xAxis: { type: 'value' },
yAxis: { type: 'category', data: catNames },
series: [
{
name: 'GMV',
type: 'bar',
data: catSales
}
]
}
// 3) 季节性趋势:按月 GMV
const seaMonths: string[] = []
const seaGmv: number[] = []
for (let i = 0; i < seasonalRows.length; i++) {
const r = seasonalRows[i]
seaMonths.push(r.getString('month') ?? '')
seaGmv.push(r.getNumber('total_gmv') ?? 0)
}
this.seasonalTrendOption = {
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 20, top: 30, bottom: 60 },
xAxis: { type: 'category', data: seaMonths },
yAxis: { type: 'value', name: 'GMV' },
series: [
{
name: 'GMV',
type: 'line',
smooth: true,
data: seaGmv
}
]
}
// 4) 价格趋势:按天平均价格
const priceDays: string[] = []
const avgPrices: number[] = []
for (let i = 0; i < priceRows.length; i++) {
const r = priceRows[i]
const d = r.getString('day') ?? ''
priceDays.push(d.length >= 10 ? d.substring(5, 10) : d)
avgPrices.push(r.getNumber('avg_price') ?? 0)
}
this.priceTrendOption = {
tooltip: { trigger: 'axis' },
grid: { left: 50, right: 20, top: 30, bottom: 60 },
xAxis: { type: 'category', data: priceDays },
yAxis: { type: 'value', name: '平均价格' },
series: [
{
name: '平均价格',
type: 'line',
smooth: true,
data: avgPrices
}
]
}
// 5) 竞争分析:商家 GMV 对比
const merchantNames: string[] = []
const merchantGmv: number[] = []
for (let i = 0; i < compRows.length; i++) {
const r = compRows[i]
merchantNames.push(r.getString('merchant_name') ?? '未知商家')
merchantGmv.push(r.getNumber('gmv') ?? 0)
}
this.competitionOption = {
tooltip: { trigger: 'item' },
legend: { top: 'bottom' },
series: [
{
name: '商家GMV',
type: 'pie',
radius: ['35%', '65%'],
center: ['50%', '50%'],
data: merchantNames.map((n, idx) => {
return { name: n, value: merchantGmv[idx] }
})
}
]
}
},
handleMenu() {