feat: 支持评估版本隔离的报告、匹配及PDF导出功能
新增revisionId参数到路由、服务及导出接口,实现不同评估版本的隔离访问。删除已废弃的需求相关服务文件,移除取消采集的融资金额、期限和用途字段,更新相关页面提示文本。同时更新文档,说明评估版本、最新资料及产品展示规则的调整,支持前台正常业务名称显示,去除环境标签。
This commit is contained in:
@@ -14,7 +14,6 @@
|
||||
<text class="product-name">{{ product!.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="product!.demo" class="demo">演示数据 · 非真实金融产品</text>
|
||||
<view class="tabs">
|
||||
<view v-if="assessmentId.length > 0" :class="tab == 'explanation' ? 'tab active' : 'tab'" @click="switchTab('explanation')"><text :class="tab == 'explanation' ? 'tab-label selected' : 'tab-label'">AI解释</text></view>
|
||||
<view :class="tab == 'details' ? 'tab active' : 'tab'" @click="switchTab('details')"><text :class="tab == 'details' ? 'tab-label selected' : 'tab-label'">产品详情</text></view>
|
||||
@@ -47,7 +46,8 @@
|
||||
<text v-else-if="match!.aiStatus == 'failed'" class="ai-note">解释生成未完成,规则依据不受影响。{{ match!.aiCanRetry ? '可以重新尝试。' : '本次已达到重试上限。' }}</text>
|
||||
<text v-else class="ai-note">当前为规则依据。可让AI逐条说明条件含义和资料核对建议。</text>
|
||||
<text v-if="aiError.length > 0" class="attention-text ai-note">{{ aiError }}</text>
|
||||
<button v-if="!aiPending && match!.aiStatus != 'completed' && (match!.aiStatus != 'failed' || match!.aiCanRetry)" class="ai-button" :disabled="aiBusy" @click="generateExplanation">{{ aiBusy ? '正在提交…' : (match!.aiStatus == 'failed' ? '重试AI解释' : '生成AI详细解释') }}</button>
|
||||
<text v-if="!match!.aiCanRequest" class="ai-note">历史版本保留已保存的规则与解释。</text>
|
||||
<button v-if="match!.aiCanRequest && !aiPending && match!.aiStatus != 'completed' && (match!.aiStatus != 'failed' || match!.aiCanRetry)" class="ai-button" :disabled="aiBusy" @click="generateExplanation">{{ aiBusy ? '正在提交…' : (match!.aiStatus == 'failed' ? '重试AI解释' : '生成AI详细解释') }}</button>
|
||||
<button v-if="aiPending && (aiError.length > 0 || pollStopped)" class="ai-button" :disabled="aiBusy" @click="checkExplanation">重新查询状态</button>
|
||||
</view>
|
||||
</template>
|
||||
@@ -84,11 +84,11 @@
|
||||
import AppHeader from '@/components/AppHeader.uvue'
|
||||
import { usePageLayout } from '@/services/layout.uts'
|
||||
import { getSession } from '@/services/api.uts'
|
||||
import { MatchView, MatchReason, ProductView, fetchMatchDetail, fetchPublicProduct, validRecordId, requestProductExplanation } from '@/packages/finance/services/matches.uts'
|
||||
import { MatchView, MatchReason, ProductView, fetchMatchDetailRecord, fetchPublicProduct, validRecordId, requestProductExplanation } from '@/packages/finance/services/matches.uts'
|
||||
import { parseCompareIds, compareSelection, saveCompareSelection, compareQuery } from '@/packages/finance/services/compare.uts'
|
||||
import { assistantRoute } from '@/packages/finance/services/assistant.uts'
|
||||
const { pageStyle, contentStyle, bottomStyle } = usePageLayout()
|
||||
const assessmentId = ref(''); const productId = ref(''); const tab = ref('details')
|
||||
const revisionId = ref(''); const assessmentId = ref(''); const productId = ref(''); const tab = ref('details')
|
||||
const selectedIds = ref<string[]>([]); let from = ''
|
||||
const product = ref<ProductView | null>(null); const match = ref<MatchView | null>(null)
|
||||
const loading = ref(false); const error = ref(''); const expanded = ref(false)
|
||||
@@ -122,8 +122,9 @@
|
||||
const request = generation; const token = getSession()?.getString('token') ?? ''
|
||||
aiError.value = ''; aiBusy.value = true
|
||||
try {
|
||||
const result = await fetchMatchDetail(assessmentId.value, productId.value)
|
||||
if (current(request, token)) { match.value = result; schedulePoll() }
|
||||
const detail = await fetchMatchDetailRecord(assessmentId.value, productId.value, revisionId.value)
|
||||
const result = detail.item
|
||||
if (current(request, token)) { revisionId.value = detail.revisionId; match.value = result; schedulePoll() }
|
||||
} catch (err) { explanationError(request, token, err instanceof Error ? err.message : '解释状态读取失败,请重试')
|
||||
} finally { if (current(request, token)) aiBusy.value = false }
|
||||
}
|
||||
@@ -136,7 +137,7 @@
|
||||
if (requestKey.length == 0) requestKey = 'explain_' + Date.now().toString() + '_' + Math.random().toString(36).slice(2, 12)
|
||||
aiBusy.value = true; aiError.value = ''; pollCount = 0; pollStopped.value = false
|
||||
try {
|
||||
const result = await requestProductExplanation(assessmentId.value, productId.value, requestKey)
|
||||
const result = await requestProductExplanation(assessmentId.value, productId.value, requestKey, revisionId.value)
|
||||
if (current(request, token)) { match.value = result; requestKey = ''; schedulePoll() }
|
||||
} catch (err) { explanationError(request, token, err instanceof Error ? err.message : 'AI解释请求失败,请重试')
|
||||
} finally { if (current(request, token)) aiBusy.value = false }
|
||||
@@ -151,38 +152,41 @@
|
||||
try {
|
||||
// 带评估编号始终读取冻结结果;404/越权/失败不能降级为公共资料。
|
||||
if (assessmentId.value.length > 0) {
|
||||
const result = await fetchMatchDetail(assessmentId.value, productId.value)
|
||||
if (visible && generation == request && getSession()?.getString('token') == token) { match.value = result; product.value = result.product; schedulePoll() }
|
||||
const detail = await fetchMatchDetailRecord(assessmentId.value, productId.value, revisionId.value)
|
||||
const result = detail.item
|
||||
if (current(request, token)) { revisionId.value = detail.revisionId; match.value = result; product.value = result.product; schedulePoll() }
|
||||
} else {
|
||||
const result = await fetchPublicProduct(productId.value)
|
||||
if (visible && generation == request && getSession()?.getString('token') == token) product.value = result
|
||||
if (current(request, token)) product.value = result
|
||||
}
|
||||
} catch (err) { if (visible && generation == request) error.value = err instanceof Error ? err.message : '产品读取失败' }
|
||||
finally { if (generation == request) loading.value = false }
|
||||
}
|
||||
const history = () => { uni.navigateTo({ url: '/packages/finance/archive/archive' }) }
|
||||
// 仅携带选择ID;助手会重新校验归属并读取冻结事实,进入页面不会调用模型。
|
||||
const consult = () => { uni.navigateTo({ url: assistantRoute(assessmentId.value, [productId.value], 'product') }) }
|
||||
function toMatches(): void { if (assessmentId.value.length > 0) uni.redirectTo({ url: '/packages/finance/matches/matches' + compareQuery(assessmentId.value, selectedIds.value) }); else history() }
|
||||
const consult = () => { uni.navigateTo({ url: assistantRoute(assessmentId.value, [productId.value], 'product', '', revisionId.value) }) }
|
||||
function toMatches(): void { if (assessmentId.value.length > 0) uni.redirectTo({ url: '/packages/finance/matches/matches' + compareQuery(assessmentId.value, selectedIds.value, revisionId.value) }); else history() }
|
||||
// 替换当前路由保留栏目、来源与对比选择,刷新后仍停留在同一栏。
|
||||
const switchTab = (value: string) => {
|
||||
if (value == tab.value) return
|
||||
const query = assessmentId.value.length > 0 ? compareQuery(assessmentId.value, selectedIds.value) + '&' : '?'
|
||||
const query = assessmentId.value.length > 0 ? compareQuery(assessmentId.value, selectedIds.value, revisionId.value) + '&' : '?'
|
||||
uni.redirectTo({ url: '/packages/finance/product/product' + query + 'productId=' + productId.value + '&tab=' + value + '&from=' + from })
|
||||
}
|
||||
// 下一步页重新核对当前可用性,避免沿用产品详情进入时的旧状态。
|
||||
const prepare = () => { if (canPrepare.value) uni.navigateTo({ url: '/packages/finance/next-step/next-step?assessmentId=' + assessmentId.value + '&productId=' + productId.value + '§ion=prepare' }) }
|
||||
const back = () => { if (getCurrentPages().length > 1) uni.navigateBack(); else if (from == 'compare') uni.redirectTo({ url: '/packages/finance/compare/compare' + compareQuery(assessmentId.value, selectedIds.value) }); else toMatches() }
|
||||
const prepare = () => { if (canPrepare.value) uni.navigateTo({ url: '/packages/finance/next-step/next-step?assessmentId=' + assessmentId.value + (revisionId.value.length > 0 ? '&revisionId=' + revisionId.value : '') + '&productId=' + productId.value + '§ion=prepare' }) }
|
||||
const back = () => { if (getCurrentPages().length > 1) uni.navigateBack(); else if (from == 'compare') uni.redirectTo({ url: '/packages/finance/compare/compare' + compareQuery(assessmentId.value, selectedIds.value, revisionId.value) }); else toMatches() }
|
||||
const openCompare = () => {
|
||||
if (match.value == null) return
|
||||
if (!selectedIds.value.includes(productId.value)) {
|
||||
if (selectedIds.value.length >= 3) { uni.showToast({ title: '已选满3项,请在对比页调整选择', icon: 'none' }); return }
|
||||
selectedIds.value.push(productId.value)
|
||||
}
|
||||
saveCompareSelection(assessmentId.value, selectedIds.value)
|
||||
uni.navigateTo({ url: '/packages/finance/compare/compare' + compareQuery(assessmentId.value, selectedIds.value) })
|
||||
saveCompareSelection(assessmentId.value, selectedIds.value, revisionId.value)
|
||||
uni.navigateTo({ url: '/packages/finance/compare/compare' + compareQuery(assessmentId.value, selectedIds.value, revisionId.value) })
|
||||
}
|
||||
onLoad((options) => {
|
||||
revisionId.value = options?.['revisionId'] ?? '';
|
||||
if (revisionId.value.length > 0 && !validRecordId(revisionId.value)) routeError = '版本编号无效,请返回重新选择。'
|
||||
assessmentId.value = options?.['assessmentId'] ?? ''; const oldId = options?.['id'] ?? ''
|
||||
productId.value = options?.['productId'] ?? oldId
|
||||
if (options?.['assessmentId'] != null && !validRecordId(assessmentId.value)) routeError = '评估编号无效,请从报告重新进入。'
|
||||
@@ -192,11 +196,11 @@
|
||||
from = options?.['from'] ?? ''
|
||||
if (!['','matches','product','compare','report','next-step','archive'].includes(from)) routeError = '页面来源参数无效,请重新选择。'
|
||||
if (options?.['productIds'] != null) {
|
||||
try { saveCompareSelection(assessmentId.value, parseCompareIds(options!['productIds']!)) }
|
||||
try { saveCompareSelection(assessmentId.value, parseCompareIds(options!['productIds']!), revisionId.value) }
|
||||
catch (err) { routeError = err instanceof Error ? err.message : '对比参数无效' }
|
||||
}
|
||||
})
|
||||
onShow(() => { visible = true; pageActive.value = true; selectedIds.value = compareSelection(assessmentId.value); load() })
|
||||
onShow(() => { visible = true; pageActive.value = true; selectedIds.value = compareSelection(assessmentId.value, revisionId.value); load() })
|
||||
onHide(() => { pageActive.value = false; visible = false; stopPoll(); generation++; product.value = null; match.value = null; error.value = ''; aiError.value = ''; aiBusy.value = false; loading.value = false; openReason.value = '' })
|
||||
onUnload(() => { pageActive.value = false; visible = false; stopPoll(); generation++; product.value = null; match.value = null; requestKey = '' })
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user