|
@@ -23,7 +23,7 @@
|
|
|
<el-card class="mb-8">
|
|
<el-card class="mb-8">
|
|
|
<h3>发票列表</h3>
|
|
<h3>发票列表</h3>
|
|
|
|
|
|
|
|
- <el-table :data="invoiceList" @selection-change="onInvoiceSelect" style="width: 100%">
|
|
|
|
|
|
|
+ <el-table :data="invoiceList" ref="tableRef" @selection-change="onInvoiceSelect" style="width: 100%">
|
|
|
<el-table-column type="selection" width="50" />
|
|
<el-table-column type="selection" width="50" />
|
|
|
<el-table-column prop="zzfphm" label="发票号" />
|
|
<el-table-column prop="zzfphm" label="发票号" />
|
|
|
<el-table-column prop="gmfmc" label="销售方" />
|
|
<el-table-column prop="gmfmc" label="销售方" />
|
|
@@ -31,6 +31,18 @@
|
|
|
<el-table-column prop="hjje" label="金额" />
|
|
<el-table-column prop="hjje" label="金额" />
|
|
|
<el-table-column prop="kprq" label="开票日期" :formatter="elTableDateFormatter" />
|
|
<el-table-column prop="kprq" label="开票日期" :formatter="elTableDateFormatter" />
|
|
|
</el-table>
|
|
</el-table>
|
|
|
|
|
+ <div class="p-5 flex justify-end">
|
|
|
|
|
+ <el-pagination
|
|
|
|
|
+ v-model:current-page="invoicePage.pageNumber"
|
|
|
|
|
+ v-model:page-size="invoicePage.pageSize"
|
|
|
|
|
+ :page-sizes="[10, 20]"
|
|
|
|
|
+ :page-count="5"
|
|
|
|
|
+ prev-text="上一页"
|
|
|
|
|
+ next-text="下一页"
|
|
|
|
|
+ layout="sizes, prev, pager, next"
|
|
|
|
|
+ :total="1000"
|
|
|
|
|
+ @change="handlePageChange" />
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
<el-button
|
|
<el-button
|
|
|
type="success"
|
|
type="success"
|
|
@@ -38,13 +50,22 @@
|
|
|
:disabled="selectedInvoices.length === 0"
|
|
:disabled="selectedInvoices.length === 0"
|
|
|
@click="doAnalyzeInvoices"
|
|
@click="doAnalyzeInvoices"
|
|
|
:loading="agentDecideLoading">
|
|
:loading="agentDecideLoading">
|
|
|
- 提交智能体分析
|
|
|
|
|
|
|
+ 个人发票智能体分析
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ type="success"
|
|
|
|
|
+ style="margin-top: 12px"
|
|
|
|
|
+ :disabled="selectedInvoices.length === 0"
|
|
|
|
|
+ @click="doSimilarIdentification"
|
|
|
|
|
+ :loading="agentSimilarLoading">
|
|
|
|
|
+ 同类发票智能体分析
|
|
|
</el-button>
|
|
</el-button>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
|
|
|
|
|
<!-- 三、智能体分析结果 -->
|
|
<!-- 三、智能体分析结果 -->
|
|
|
<el-card v-if="agentResult" class="mb-8">
|
|
<el-card v-if="agentResult" class="mb-8">
|
|
|
- <h3>智能体分析结果</h3>
|
|
|
|
|
|
|
+ <h3>智能体个人发票分析结果</h3>
|
|
|
|
|
|
|
|
<el-tag :type="decisionTag">
|
|
<el-tag :type="decisionTag">
|
|
|
{{ agentResult.decision }}
|
|
{{ agentResult.decision }}
|
|
@@ -72,10 +93,37 @@
|
|
|
</div>
|
|
</div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
+ <el-card class="analysis-result-card" v-if="similarAgentResult">
|
|
|
|
|
+ <!-- 分析结果 -->
|
|
|
|
|
+ <h3>智能体同类识别结果</h3>
|
|
|
|
|
+
|
|
|
|
|
+ <el-tag :type="similarDecisionTag">
|
|
|
|
|
+ {{ similarAgentResult.decision }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+
|
|
|
|
|
+ <el-tag style="margin-left: 8px">置信度:{{ similarAgentResult.confidence }}</el-tag>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="mt-8">
|
|
|
|
|
+ {{ similarAgentResult.summary }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-divider v-if="hasSimilarEvidence">证据链(引用)</el-divider>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 证据链 -->
|
|
|
|
|
+ <el-timeline v-if="hasSimilarEvidence">
|
|
|
|
|
+ <el-timeline-item v-for="evidence in similarAgentResult.evidence_chain" :key="Math.random()">
|
|
|
|
|
+ <strong>信息引用</strong>
|
|
|
|
|
+ <p>{{ evidence.summary }}</p>
|
|
|
|
|
+ <el-tag style="margin-top: 4px">置信度:{{ evidence.confidence }}</el-tag>
|
|
|
|
|
+ <el-tag style="margin-top: 4px; margin-left: 4px">引用来源:{{ evidence.source }}</el-tag>
|
|
|
|
|
+ </el-timeline-item>
|
|
|
|
|
+ </el-timeline>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
<el-card v-if="allEvidenceItems.length > 0" class="mb-8 danger-block" v-loading="approveLoading">
|
|
<el-card v-if="allEvidenceItems.length > 0" class="mb-8 danger-block" v-loading="approveLoading">
|
|
|
<h3>风控知识人工审核</h3>
|
|
<h3>风控知识人工审核</h3>
|
|
|
|
|
|
|
|
- <el-tabs v-model="activeTab">
|
|
|
|
|
|
|
+ <el-tabs v-model="activeKnowledgeTab">
|
|
|
<el-tab-pane name="RULE">
|
|
<el-tab-pane name="RULE">
|
|
|
<template #label>
|
|
<template #label>
|
|
|
<el-badge :value="tabCounts.RULE" :hidden="tabCounts.RULE === 0">
|
|
<el-badge :value="tabCounts.RULE" :hidden="tabCounts.RULE === 0">
|
|
@@ -141,9 +189,10 @@
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
import { searchInvoicesAPI } from "@/api/invoice";
|
|
import { searchInvoicesAPI } from "@/api/invoice";
|
|
|
-import { analyzeInvoices, evidenceSearch } from "@/api/risk";
|
|
|
|
|
|
|
+import { analyzeInvoices, evidenceSearch, similarIdentificationApi } from "@/api/risk";
|
|
|
import { storeCases, storeIndustries, storeRules, storeSignals } from "@/api/vectorStore";
|
|
import { storeCases, storeIndustries, storeRules, storeSignals } from "@/api/vectorStore";
|
|
|
-import type { AgentResult, Invoice, ReviewItem } from "@/types/agent";
|
|
|
|
|
|
|
+import type { AgentResult, Invoice, ReviewItem, SimilarIdentificationResult } from "@/types/agent";
|
|
|
|
|
+import type { SystemPage } from "@/types/index";
|
|
|
import { formatDateTime } from "@/utils/dateutils";
|
|
import { formatDateTime } from "@/utils/dateutils";
|
|
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
import { computed, reactive, ref } from "vue";
|
|
import { computed, reactive, ref } from "vue";
|
|
@@ -156,18 +205,27 @@ const query = reactive({
|
|
|
|
|
|
|
|
const invoiceList = ref<Invoice[]>([]);
|
|
const invoiceList = ref<Invoice[]>([]);
|
|
|
const selectedInvoices = ref<Invoice[]>([]);
|
|
const selectedInvoices = ref<Invoice[]>([]);
|
|
|
-
|
|
|
|
|
|
|
+const invoicePage = reactive<SystemPage>({
|
|
|
|
|
+ pageNumber: 1,
|
|
|
|
|
+ pageSize: 10,
|
|
|
|
|
+});
|
|
|
|
|
+const tableRef = ref<any>(null);
|
|
|
/* ---------------- 智能体结果 ---------------- */
|
|
/* ---------------- 智能体结果 ---------------- */
|
|
|
const agentDecideLoading = ref(false);
|
|
const agentDecideLoading = ref(false);
|
|
|
const evidenceReplenishLoading = ref(false);
|
|
const evidenceReplenishLoading = ref(false);
|
|
|
const approveLoading = ref(false);
|
|
const approveLoading = ref(false);
|
|
|
|
|
+const agentSimilarLoading = ref(false);
|
|
|
|
|
|
|
|
const agentResult = ref<AgentResult | null>(null);
|
|
const agentResult = ref<AgentResult | null>(null);
|
|
|
const agentEvidenceResult = ref<any | null>(null);
|
|
const agentEvidenceResult = ref<any | null>(null);
|
|
|
|
|
|
|
|
|
|
+const similarAgentResult = ref<SimilarIdentificationResult | null>(null);
|
|
|
/* ---------------- 计算属性 ---------------- */
|
|
/* ---------------- 计算属性 ---------------- */
|
|
|
|
|
|
|
|
const hasEvidence = computed(() => agentResult.value && agentResult.value.completion.evidence_chain.length > 0);
|
|
const hasEvidence = computed(() => agentResult.value && agentResult.value.completion.evidence_chain.length > 0);
|
|
|
|
|
+const hasSimilarEvidence = computed(
|
|
|
|
|
+ () => similarAgentResult.value && similarAgentResult.value.evidence_chain.length > 0,
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
// const needManualReview = computed(() => agentResult.value?.decision === "UNCERTAIN");
|
|
// const needManualReview = computed(() => agentResult.value?.decision === "UNCERTAIN");
|
|
|
|
|
|
|
@@ -178,13 +236,19 @@ const decisionTag = computed(() => {
|
|
|
return "success";
|
|
return "success";
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+const similarDecisionTag = computed(() => {
|
|
|
|
|
+ if (!similarAgentResult.value) return "info";
|
|
|
|
|
+ if (similarAgentResult.value.decision === "NOT_BELONG") return "danger";
|
|
|
|
|
+ return "success";
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
/* ---------------- 方法 ---------------- */
|
|
/* ---------------- 方法 ---------------- */
|
|
|
const elTableDateFormatter = (_: any, __: any, cellValue: string) => {
|
|
const elTableDateFormatter = (_: any, __: any, cellValue: string) => {
|
|
|
return formatDateTime(cellValue);
|
|
return formatDateTime(cellValue);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const searchInvoices = async () => {
|
|
const searchInvoices = async () => {
|
|
|
- searchInvoicesAPI(query)
|
|
|
|
|
|
|
+ searchInvoicesAPI(query, invoicePage)
|
|
|
.then((data) => {
|
|
.then((data) => {
|
|
|
invoiceList.value = data.data;
|
|
invoiceList.value = data.data;
|
|
|
ElMessage.success("发票查询成功");
|
|
ElMessage.success("发票查询成功");
|
|
@@ -193,15 +257,33 @@ const searchInvoices = async () => {
|
|
|
throw new Error(`Fetch error: ${error.message}`);
|
|
throw new Error(`Fetch error: ${error.message}`);
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
|
|
+const handlePageChange = async () => {
|
|
|
|
|
+ await searchInvoices();
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
searchInvoices();
|
|
searchInvoices();
|
|
|
|
|
|
|
|
const onInvoiceSelect = (rows: Invoice[]) => {
|
|
const onInvoiceSelect = (rows: Invoice[]) => {
|
|
|
- selectedInvoices.value = rows;
|
|
|
|
|
-};
|
|
|
|
|
|
|
+ if (rows.length > 1) {
|
|
|
|
|
+ ElMessage.warning("仅支持单选发票进行分析,已自动保留第一条记录")
|
|
|
|
|
+
|
|
|
|
|
+ const firstRow = rows[0] as Invoice
|
|
|
|
|
+
|
|
|
|
|
+ tableRef.value?.clearSelection()
|
|
|
|
|
+ tableRef.value?.toggleRowSelection(firstRow, true)
|
|
|
|
|
+
|
|
|
|
|
+ selectedInvoices.value = [firstRow]
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ selectedInvoices.value = rows ?? null
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
const doAnalyzeInvoices = async () => {
|
|
const doAnalyzeInvoices = async () => {
|
|
|
agentEvidenceResult.value = null;
|
|
agentEvidenceResult.value = null;
|
|
|
agentResult.value = null;
|
|
agentResult.value = null;
|
|
|
|
|
+ similarAgentResult.value = null;
|
|
|
allEvidenceItems.value = [];
|
|
allEvidenceItems.value = [];
|
|
|
agentDecideLoading.value = true;
|
|
agentDecideLoading.value = true;
|
|
|
analyzeInvoices(selectedInvoices.value[0])
|
|
analyzeInvoices(selectedInvoices.value[0])
|
|
@@ -217,6 +299,25 @@ const doAnalyzeInvoices = async () => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const doSimilarIdentification = async () => {
|
|
|
|
|
+ agentEvidenceResult.value = null;
|
|
|
|
|
+ agentResult.value = null;
|
|
|
|
|
+ similarAgentResult.value = null;
|
|
|
|
|
+ allEvidenceItems.value = [];
|
|
|
|
|
+ agentSimilarLoading.value = true;
|
|
|
|
|
+ similarIdentificationApi(selectedInvoices.value[0])
|
|
|
|
|
+ .then((data) => {
|
|
|
|
|
+ similarAgentResult.value = data.data;
|
|
|
|
|
+ ElMessage.success("智能体分析成功");
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch((error) => {
|
|
|
|
|
+ throw new Error(`Fetch error: ${error.message}`);
|
|
|
|
|
+ })
|
|
|
|
|
+ .finally(() => {
|
|
|
|
|
+ agentSimilarLoading.value = false;
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const evidenceReplenish = async () => {
|
|
const evidenceReplenish = async () => {
|
|
|
evidenceReplenishLoading.value = true;
|
|
evidenceReplenishLoading.value = true;
|
|
|
evidenceSearch(selectedInvoices.value[0])
|
|
evidenceSearch(selectedInvoices.value[0])
|
|
@@ -238,7 +339,7 @@ const evidenceReplenish = async () => {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
/* ---------------- 风控知识人工审核 ---------------- */
|
|
/* ---------------- 风控知识人工审核 ---------------- */
|
|
|
-const activeTab = ref<"CASE" | "INDUSTRY" | "RULE" | "SIGNAL">("RULE");
|
|
|
|
|
|
|
+const activeKnowledgeTab = ref<"CASE" | "INDUSTRY" | "RULE" | "SIGNAL">("RULE");
|
|
|
|
|
|
|
|
const allEvidenceItems = ref<ReviewItem[]>([]);
|
|
const allEvidenceItems = ref<ReviewItem[]>([]);
|
|
|
const tabCounts = computed(() => ({
|
|
const tabCounts = computed(() => ({
|
|
@@ -295,7 +396,7 @@ const buildSignals = (signals: any[]): ReviewItem[] =>
|
|
|
}));
|
|
}));
|
|
|
|
|
|
|
|
/* 当前 Tab 数据 */
|
|
/* 当前 Tab 数据 */
|
|
|
-const currentItems = computed(() => allEvidenceItems.value.filter((i) => i.type === activeTab.value));
|
|
|
|
|
|
|
+const currentItems = computed(() => allEvidenceItems.value.filter((i) => i.type === activeKnowledgeTab.value));
|
|
|
|
|
|
|
|
/* 操作 */
|
|
/* 操作 */
|
|
|
const approve = async (item: ReviewItem) => {
|
|
const approve = async (item: ReviewItem) => {
|