first commit

This commit is contained in:
2025-12-16 12:33:02 +08:00
commit 547f543e6c
643 changed files with 87387 additions and 0 deletions

179
src/views/Report.vue Normal file
View File

@@ -0,0 +1,179 @@
<template>
<BaseReport v-if="queryState === 'success'" :order-id="orderId" :order-no="orderNo" :feature="feature"
:reportData="reportData" :reportParams="reportParams" :reportName="reportName" :reportDateTime="reportDateTime"
:isEmpty="isEmpty" :isDone="isDone" :isExample="false" />
<div v-else-if="queryState === 'pending'" class="loading-container">
<div class="loading-spinner"></div>
<p>报告生成中请稍候...</p>
</div>
<div class="p-4" v-else-if="queryState === 'failed'">
<LEmpty />
</div>
</template>
<script setup>
import LEmpty from "@/components/LEmpty.vue";
import { aesDecrypt } from "@/utils/crypto";
const AES_KEY = import.meta.env.VITE_INQUIRE_AES_KEY;
const route = useRoute();
const feature = ref("");
const reportData = ref([]);
const reportParams = ref({});
const reportName = ref("");
const reportDateTime = ref(null);
const isEmpty = ref(false);
const isDone = ref(false);
const orderId = ref(null);
const orderNo = ref("");
const queryState = ref("");
const pollingInterval = ref(null);
onBeforeMount(() => {
const query = new URLSearchParams(window.location.search);
orderNo.value = query.get("out_trade_no");
orderId.value = query.get("order_id");
if (!orderNo.value && !orderId.value) {
orderId.value = route.query.orderId;
orderNo.value = route.query.orderNo;
}
if (!orderId.value && !orderNo.value) return;
getReport();
});
onBeforeUnmount(() => {
if (pollingInterval.value) {
clearInterval(pollingInterval.value);
}
});
const getReport = async () => {
let queryUrl = "";
if (orderNo.value) {
queryUrl = `/query/orderNo/${orderNo.value}`;
} else if (orderId.value) {
queryUrl = `/query/orderId/${orderId.value}`;
} else {
return;
}
const { data, error } = await useApiFetch(queryUrl).get().json();
if (!AES_KEY) {
console.error("缺少解密密钥");
isEmpty.value = true;
isDone.value = true;
return;
}
if (data.value && !error.value) {
if (data.value.code === 200) {
let decryptedData = data.value.data;
if (typeof decryptedData === "string") {
try {
const decryptedStr = aesDecrypt(decryptedData, AES_KEY);
decryptedData = JSON.parse(decryptedStr);
} catch (err) {
console.error("报告数据解密失败", err);
isEmpty.value = true;
isDone.value = true;
return;
}
}
if (!decryptedData) {
isEmpty.value = true;
isDone.value = true;
return;
}
queryState.value = decryptedData.query_state;
if (queryState.value === "success") {
feature.value = decryptedData.product || "";
const sortedQueryData = Array.isArray(decryptedData.query_data)
? [...decryptedData.query_data].sort((a, b) => {
return a.feature.sort - b.feature.sort;
})
: [];
reportData.value = sortedQueryData;
reportParams.value = decryptedData.query_params || {};
reportName.value = decryptedData.product_name || "";
reportDateTime.value = decryptedData.create_time || null;
isDone.value = true;
// 如果成功,清除轮询
if (pollingInterval.value) {
clearInterval(pollingInterval.value);
pollingInterval.value = null;
}
} else if (queryState.value === "pending") {
// 如果是pending状态且没有轮询启动轮询
if (!pollingInterval.value) {
pollingInterval.value = setInterval(() => {
getReport();
}, 2000); // 每2秒轮询一次
}
} else if (queryState.value === "failed") {
isEmpty.value = true;
isDone.value = true;
// 如果失败,清除轮询
if (pollingInterval.value) {
clearInterval(pollingInterval.value);
pollingInterval.value = null;
}
}
} else if (data.value.code === 200003) {
isEmpty.value = true;
isDone.value = true;
} else if (data.value.code === 200002) {
isPending.value = true;
isDone.value = true;
}
}
};
</script>
<style lang="scss" scoped>
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
.loading-spinner {
width: 50px;
height: 50px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
p {
color: #666;
font-size: 16px;
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>