Files
jjx-wap-sign-only/pages/ai/reports.vue
T

264 lines
6.5 KiB
Vue

<template>
<view class="page-wrap">
<uititle title="我的报告"></uititle>
<!-- 查询按钮 -->
<view class="action-bar">
<u-button type="primary" size="small" shape="circle" @click="queryReports">
🔍 查询报告
</u-button>
</view>
<!-- 空状态 -->
<view v-if="reportList.length === 0 && !loading" class="empty-box">
<view class="empty-icon">📋</view>
<view class="empty-text">暂无审查报告</view>
<view class="empty-hint">点击上方「查询报告」从服务器加载</view>
</view>
<!-- 加载中 -->
<view v-if="loading" class="loading-box">
<u-loading-icon size="48"></u-loading-icon>
<text class="loading-text">加载中...</text>
</view>
<!-- 报告列表 -->
<view v-for="(r, i) in reportList" :key="i" class="report-item">
<view class="report-header">
<view class="report-name">📄 {{ r.fileName }}</view>
<view :class="['status-tag', 'status-' + r.status]">{{ statusMap[r.status] || r.status }}</view>
</view>
<view class="report-meta">{{ r.time }} · 消耗 {{ r.amount }} 积分</view>
<view class="report-actions">
<view v-if="r.htmlUrl" class="action-btn" @click="openUrl(r.htmlUrl)">📄 查看报告</view>
<view v-if="r.fileUrl" class="action-btn" @click="openUrl(r.fileUrl)">📎 查看文件</view>
<view v-if="r.status === 'completed' && r.result" class="action-btn" @click="viewSummary(r)">📋 查看摘要</view>
</view>
</view>
<!-- 摘要弹窗 -->
<u-popup :show="showSummary" mode="bottom" @close="showSummary = false" round="24">
<view class="summary-popup">
<view class="summary-title">📋 审查摘要</view>
<scroll-view scroll-y class="summary-body">
<rich-text :nodes="summaryContent"></rich-text>
</scroll-view>
<view class="summary-actions">
<u-button v-if="summaryHtmlUrl" type="primary" size="small" @click="openUrl(summaryHtmlUrl)">查看完整报告</u-button>
<u-button size="small" @click="showSummary = false">关闭</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import uititle from '@/components/common/title'
import dayjs from 'dayjs'
export default {
components: { uititle },
data() {
return {
reportList: [],
loading: false,
showSummary: false,
summaryContent: '',
summaryHtmlUrl: '',
statusMap: {
uploaded: '待审查',
reviewing: '排队中',
processing: '审查中',
completed: '已完成',
failed: '失败'
}
}
},
onShow() {
this.queryReports()
},
methods: {
async queryReports() {
this.loading = true
try {
const res = await vk.callFunction({
url: 'client/ai/pub/getMyReports',
data: { pageIndex: 1, pageSize: 50 }
})
if (res.code === 0 && res.data && res.data.rows) {
this.reportList = res.data.rows.map(r => ({
fileName: r.file_name,
fileSize: r.file_size,
fileUrl: r.file_url || '',
htmlUrl: r.html_url || '',
time: dayjs(r._add_time && r._add_time['$date'] ? r._add_time['$date'] : r._add_time).format('YYYY-MM-DD HH:mm'),
amount: r.points_used || 0,
result: r.result,
status: r.status,
recordId: r._id
}))
}
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
}
this.loading = false
},
viewSummary(r) {
this.summaryContent = this.formatContent(r.result)
this.summaryHtmlUrl = r.htmlUrl
this.showSummary = true
},
formatContent(text) {
if (!text) return '<p>暂无审查结果</p>'
let html = text
html = html.replace(/^###\s+(.+)$/gm, '<h4 style="font-size:15px;margin:12px 0 6px;">$1</h4>')
html = html.replace(/^##\s+(.+)$/gm, '<h3 style="font-size:16px;margin:16px 0 8px;">$1</h3>')
html = html.replace(/\*\*(.*?)\*\*/g, '<strong style="color:#a92924;">$1</strong>')
html = html.replace(/^[\-\•]\s+(.+)$/gm, '<div style="padding-left:12px;">• $1</div>')
html = html.replace(/^(\d+)\.\s+(.+)$/gm, '<div style="padding-left:12px;">$1. $2</div>')
html = html.replace(/\n/g, '<br>')
return html
},
openUrl(url) {
if (url) {
// #ifdef H5
window.open(url, '_blank')
// #endif
// #ifndef H5
uni.navigateTo({ url: '/pages/ai/webview?url=' + encodeURIComponent(url) })
// #endif
}
}
}
}
</script>
<style lang="scss" scoped>
.page-wrap {
min-height: 100vh;
background: #f5f5f5;
}
.action-bar {
padding: 20rpx 30rpx;
display: flex;
justify-content: flex-end;
}
.empty-box {
text-align: center;
padding: 120rpx 30rpx;
}
.empty-icon {
font-size: 80rpx;
}
.empty-text {
font-size: 30rpx;
font-weight: 600;
margin-top: 16rpx;
}
.empty-hint {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
.loading-box {
text-align: center;
padding: 60rpx 0;
}
.loading-text {
display: block;
font-size: 28rpx;
margin-top: 16rpx;
}
.report-item {
background: #fff;
margin: 16rpx 30rpx;
padding: 24rpx;
border-radius: 12rpx;
}
.report-header {
display: flex;
align-items: center;
gap: 16rpx;
}
.report-name {
flex: 1;
font-size: 28rpx;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.status-tag {
font-size: 22rpx;
padding: 4rpx 16rpx;
border-radius: 8rpx;
flex-shrink: 0;
}
.status-uploaded { background: #f0f0f0; color: #999; }
.status-reviewing { background: #fdf6ec; color: #e6a23c; }
.status-processing { background: #fdf6ec; color: #e6a23c; }
.status-completed { background: #f0f9eb; color: #67c23a; }
.status-failed { background: #fef0f0; color: #f56c6c; }
.report-meta {
font-size: 22rpx;
color: #999;
margin-top: 8rpx;
}
.report-actions {
display: flex;
gap: 16rpx;
margin-top: 16rpx;
flex-wrap: wrap;
}
.action-btn {
font-size: 24rpx;
color: #a92924;
padding: 8rpx 20rpx;
border: 1rpx solid #a92924;
border-radius: 8rpx;
}
.action-btn:active {
background: #a92924;
color: #fff;
}
.summary-popup {
padding: 30rpx;
max-height: 70vh;
}
.summary-title {
font-size: 32rpx;
font-weight: 600;
margin-bottom: 20rpx;
}
.summary-body {
max-height: 50vh;
font-size: 26rpx;
line-height: 1.8;
}
.summary-actions {
display: flex;
gap: 16rpx;
margin-top: 24rpx;
justify-content: center;
}
</style>