294 lines
7.7 KiB
Plaintext
294 lines
7.7 KiB
Plaintext
<template>
|
|
<view class="page" @click="closeMoreMenu">
|
|
<AnalyticsTopBar
|
|
:title="'数据洞察详情'"
|
|
:lastUpdateTime="lastUpdateTime"
|
|
:sidebarVisible="showSidebarMenu"
|
|
@menu-click="handleMenu"
|
|
@refresh="refreshData"
|
|
@search="handleSearch"
|
|
@notification="handleNotification"
|
|
@fullscreen="handleFullscreen"
|
|
@mobile="handleMobile"
|
|
@dropdown="handleDropdown"
|
|
@settings="handleSettings"
|
|
/>
|
|
|
|
<view class="page-layout">
|
|
<AnalyticsSidebarMenu
|
|
:visible="showSidebarMenu"
|
|
:currentPath="currentPath"
|
|
@visible-change="handleSidebarUpdate"
|
|
/>
|
|
|
|
<view class="main-content">
|
|
<view class="container">
|
|
<view class="card card-full">
|
|
<view class="card-head">
|
|
<text class="card-title">{{ insight.title || '洞察详情' }}</text>
|
|
<view class="meta-row">
|
|
<text class="badge" :class="'badge-' + (insight.type || 'info')">{{ getInsightTypeText(insight.type) }}</text>
|
|
<text class="badge badge-impact" :class="'impact-' + (insight.impact || 'medium')">{{ getImpactText(insight.impact) }}</text>
|
|
<text class="meta-time" v-if="insight.created_at">{{ formatTime(insight.created_at) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="loading" class="state">
|
|
<text class="state-text">加载中...</text>
|
|
</view>
|
|
<view v-else-if="errorMsg" class="state">
|
|
<text class="state-text">{{ errorMsg }}</text>
|
|
</view>
|
|
<view v-else class="content">
|
|
<text class="content-text">{{ insight.content }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="card" v-if="relatedReport.id">
|
|
<view class="card-head">
|
|
<text class="card-title">关联报表</text>
|
|
<text class="card-desc">{{ relatedReport.type }} · {{ relatedReport.period }}</text>
|
|
</view>
|
|
<view class="report-row" @click="goToReportDetail">
|
|
<view class="report-icon">📄</view>
|
|
<view class="report-info">
|
|
<text class="report-title">{{ relatedReport.title }}</text>
|
|
<text class="report-time">{{ relatedReport.generated_at ? formatTime(relatedReport.generated_at) : '' }}</text>
|
|
</view>
|
|
<text class="report-arrow">></text>
|
|
</view>
|
|
</view>
|
|
|
|
<view style="height: 24px;"></view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { reactive, ref } from 'vue'
|
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
|
|
|
import AnalyticsSidebarMenu from '@/components/analytics/AnalyticsSidebarMenu.uvue'
|
|
import AnalyticsTopBar from '@/components/analytics/AnalyticsTopBar.uvue'
|
|
|
|
import { fetchInsightDetail, fetchRelatedReport } from '@/services/analytics/insightDetailService.uts'
|
|
import { mapAnalyticsError } from '@/services/analytics/errorMapper.uts'
|
|
|
|
import type { InsightDetail, RelatedReport } from '@/types/analytics/insight.uts'
|
|
|
|
const lastUpdateTime = ref('')
|
|
const showMoreMenu = ref(false)
|
|
const showSidebarMenu = ref(false)
|
|
const currentPath = ref('/pages/mall/analytics/insight-detail')
|
|
const insightId = ref('')
|
|
const loading = ref(false)
|
|
const errorMsg = ref('')
|
|
|
|
const insight = reactive<InsightDetail>({
|
|
id: '',
|
|
report_id: '',
|
|
type: 'info',
|
|
impact: 'medium',
|
|
title: '',
|
|
content: '',
|
|
created_at: ''
|
|
})
|
|
|
|
const relatedReport = reactive<RelatedReport>({
|
|
id: '',
|
|
title: '',
|
|
type: '',
|
|
period: '',
|
|
generated_at: ''
|
|
})
|
|
|
|
onLoad((options: any) => {
|
|
currentPath.value = '/pages/mall/analytics/insight-detail'
|
|
updateTime()
|
|
|
|
const iid = (options.insightId || options.id) as string
|
|
if (!iid) {
|
|
uni.showToast({ title: '缺少洞察ID', icon: 'none' })
|
|
setTimeout(() => uni.navigateBack(), 1500)
|
|
return
|
|
}
|
|
|
|
insightId.value = iid
|
|
void loadInsightDetail()
|
|
})
|
|
|
|
onShow(() => {
|
|
currentPath.value = '/pages/mall/analytics/insight-detail'
|
|
})
|
|
|
|
async function loadInsightDetail() {
|
|
try {
|
|
loading.value = true
|
|
errorMsg.value = ''
|
|
updateTime()
|
|
|
|
const data = await fetchInsightDetail(insightId.value)
|
|
if (data == null) {
|
|
errorMsg.value = '洞察不存在或无权限访问'
|
|
return
|
|
}
|
|
|
|
insight.id = data.id
|
|
insight.report_id = data.report_id
|
|
insight.type = data.type
|
|
insight.impact = data.impact
|
|
insight.title = data.title
|
|
insight.content = data.content
|
|
insight.created_at = data.created_at
|
|
|
|
relatedReport.id = ''
|
|
relatedReport.title = ''
|
|
relatedReport.type = ''
|
|
relatedReport.period = ''
|
|
relatedReport.generated_at = ''
|
|
|
|
if (insight.report_id) {
|
|
try {
|
|
const related = await fetchRelatedReport(insight.report_id)
|
|
if (related != null) {
|
|
relatedReport.id = related.id
|
|
relatedReport.title = related.title
|
|
relatedReport.type = related.type
|
|
relatedReport.period = related.period
|
|
relatedReport.generated_at = related.generated_at
|
|
}
|
|
} catch (e) {
|
|
console.error('loadInsightDetail related report error', e)
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('loadInsightDetail failed', e)
|
|
errorMsg.value = mapAnalyticsError(e, { fallbackMessage: '加载失败,请稍后重试' })
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function refreshData() {
|
|
void loadInsightDetail()
|
|
uni.showToast({ title: '已刷新', icon: 'success' })
|
|
}
|
|
|
|
function exportReport() {
|
|
uni.showActionSheet({
|
|
itemList: ['导出Excel', '导出PDF', '导出图片'],
|
|
success: () => uni.showToast({ title: '导出成功', icon: 'success' })
|
|
})
|
|
}
|
|
|
|
function updateTime() {
|
|
const now = new Date()
|
|
const hh = now.getHours().toString().padStart(2, '0')
|
|
const mm = now.getMinutes().toString().padStart(2, '0')
|
|
lastUpdateTime.value = `${hh}:${mm}`
|
|
}
|
|
|
|
function formatTime(timeStr: string): string {
|
|
if (!timeStr) return ''
|
|
return `${timeStr}`.replace('T', ' ').split('.')[0]
|
|
}
|
|
|
|
function getInsightTypeText(type: string): string {
|
|
const t = `${type || 'info'}`
|
|
const map: Record<string, string> = {
|
|
positive: '正向',
|
|
warning: '预警',
|
|
negative: '风险',
|
|
info: '信息'
|
|
}
|
|
return map[t] || '信息'
|
|
}
|
|
|
|
function getImpactText(impact: string): string {
|
|
const impacts: Record<string, string> = {
|
|
high: '高影响',
|
|
medium: '中影响',
|
|
low: '低影响'
|
|
}
|
|
return impacts[impact || 'medium'] || '中影响'
|
|
}
|
|
|
|
function goToReportDetail() {
|
|
if (!relatedReport.id) return
|
|
uni.navigateTo({
|
|
url: `/pages/mall/analytics/report-detail?reportId=${relatedReport.id}`
|
|
})
|
|
}
|
|
|
|
function handleMenu() {
|
|
showSidebarMenu.value = true
|
|
}
|
|
|
|
function handleSidebarUpdate(visible: boolean) {
|
|
showSidebarMenu.value = visible
|
|
}
|
|
|
|
function toggleMoreMenu() {
|
|
showMoreMenu.value = !showMoreMenu.value
|
|
}
|
|
|
|
function closeMoreMenu() {
|
|
showMoreMenu.value = false
|
|
}
|
|
|
|
function handleSearch() {
|
|
uni.showToast({ title: '搜索', icon: 'none' })
|
|
}
|
|
|
|
function handleNotification() {
|
|
uni.showToast({ title: '通知', icon: 'none' })
|
|
}
|
|
|
|
function handleFullscreen() {
|
|
uni.showToast({ title: '全屏', icon: 'none' })
|
|
}
|
|
|
|
function handleMobile() {
|
|
uni.showToast({ title: '移动端', icon: 'none' })
|
|
}
|
|
|
|
function handleDropdown() {
|
|
uni.showToast({ title: '下拉菜单', icon: 'none' })
|
|
}
|
|
|
|
function handleSettings() {
|
|
uni.showToast({ title: '设置', icon: 'none' })
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.page {
|
|
min-height: 100vh;
|
|
background: #f6f7fb;
|
|
}
|
|
|
|
.page-layout {
|
|
display: flex;
|
|
flex-direction: row !important;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.main-content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding-top: 64px;
|
|
}
|
|
|
|
.container {
|
|
width: 100%;
|
|
max-width: 1280px;
|
|
margin: 0 auto;
|
|
padding: 16px 16px 28px;
|
|
box-sizing: border-box;
|
|
}
|
|
</style>
|