This commit is contained in:
liangzai
2025-05-02 14:52:22 +08:00
parent 6de14256b4
commit 1eaf3e810e
51 changed files with 15973 additions and 2321 deletions

View File

@@ -10,11 +10,11 @@
<title>天远查 - 婚恋评估、司法涉诉查询、婚姻状态、判决书查询工具</title>
<meta
name="description"
content="天远查提供婚恋评估报告、司法涉诉查询、婚姻状态查询、判决书查询、失信人、个人涉诉、企业涉诉、车辆核验等多项服务,帮助您查询婚姻信息、名下车辆、涉诉风险等,提供全面的法律与金融风险防范工具。"
content="天远查依托大数据技术精准查询司法涉诉风险AI律师智能分析企业/个人司法风险,支持婚姻状况/婚姻状态/判决书/被执行人/老赖/失信人/限高查询,并提供身份/银行卡/手机号码一键核验AI律师在线咨询+风险报告。多维防控财产/信用/身份风险!"
/>
<meta
name="keywords"
content="婚恋评估, 司法涉诉查询, 判决书查询, 婚姻状态查询, 失信人, 个人涉诉查询, 企业涉诉查询, 名下车辆核验, 车辆核验, 婚姻报告, 法律风险, 信用风险, 银行卡黑名单, 手机身份证核验, 学历核验, AI律师"
content="婚恋评估, 司法涉诉查询, 判决书查询, 婚姻状态查询, 老赖, 被执行人, 限高, 失信人, 个人涉诉查询, 企业涉诉查询, 名下车辆核验, 车辆核验, 婚姻状况, 风险报告, 法律风险, 信用风险, 银行卡黑名单, 手机身份证核验, 学历核验, AI律师, 在线查询"
/>
<meta name="author" content="天远查" />
<meta

View File

@@ -17,21 +17,22 @@
"nprogress": "^0.2.0",
"pinia": "^2.2.6",
"vant": "^4.9.9",
"vue": "^3.5.12",
"vue": "^3.5.13",
"vue-echarts": "^7.0.3",
"vue-router": "^4.4.5"
},
"devDependencies": {
"@vant/auto-import-resolver": "^1.2.1",
"@vitejs/plugin-vue": "^5.1.4",
"@vitejs/plugin-vue-jsx": "^4.0.1",
"@vitejs/plugin-vue": "^5.2.0",
"@vitejs/plugin-vue-jsx": "^4.1.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
"sass-embedded": "^1.81.0",
"tailwindcss": "^3.4.15",
"unplugin-auto-import": "^0.18.5",
"unplugin-vue-components": "^0.27.5",
"vite": "^5.4.10",
"vite-plugin-vue-devtools": "^7.5.4"
}
"vite": "^5.4.11",
"vite-plugin-vue-devtools": "^7.6.4"
},
"packageManager": "pnpm@10.9.0+sha512.0486e394640d3c1fb3c9d43d49cf92879ff74f8516959c235308f5a8f62e2e19528a65cdc2a3058f587cde71eba3d5b56327c8c33a97e4c4051ca48a10ca2d5f"
}

20
pnpm-lock.yaml generated
View File

@@ -33,7 +33,7 @@ importers:
specifier: ^4.9.9
version: 4.9.9(vue@3.5.13)
vue:
specifier: ^3.5.12
specifier: ^3.5.13
version: 3.5.13
vue-echarts:
specifier: ^7.0.3
@@ -46,10 +46,10 @@ importers:
specifier: ^1.2.1
version: 1.2.1
'@vitejs/plugin-vue':
specifier: ^5.1.4
specifier: ^5.2.0
version: 5.2.0(vite@5.4.11(sass-embedded@1.81.0))(vue@3.5.13)
'@vitejs/plugin-vue-jsx':
specifier: ^4.0.1
specifier: ^4.1.0
version: 4.1.0(vite@5.4.11(sass-embedded@1.81.0))(vue@3.5.13)
autoprefixer:
specifier: ^10.4.20
@@ -70,10 +70,10 @@ importers:
specifier: ^0.27.5
version: 0.27.5(@babel/parser@7.26.2)(rollup@4.27.3)(vue@3.5.13)
vite:
specifier: ^5.4.10
specifier: ^5.4.11
version: 5.4.11(sass-embedded@1.81.0)
vite-plugin-vue-devtools:
specifier: ^7.5.4
specifier: ^7.6.4
version: 7.6.4(rollup@4.27.3)(vite@5.4.11(sass-embedded@1.81.0))(vue@3.5.13)
packages:
@@ -691,8 +691,8 @@ packages:
resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
engines: {node: '>= 6'}
caniuse-lite@1.0.30001680:
resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==}
caniuse-lite@1.0.30001713:
resolution: {integrity: sha512-wCIWIg+A4Xr7NfhTuHdX+/FKh3+Op3LBbSp2N5Pfx6T/LhdQy3GTyoTg48BReaW/MyMNZAkTadsBtai3ldWK0Q==}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
@@ -2222,7 +2222,7 @@ snapshots:
autoprefixer@10.4.20(postcss@8.4.49):
dependencies:
browserslist: 4.24.2
caniuse-lite: 1.0.30001680
caniuse-lite: 1.0.30001713
fraction.js: 4.3.7
normalize-range: 0.1.2
picocolors: 1.1.1
@@ -2253,7 +2253,7 @@ snapshots:
browserslist@4.24.2:
dependencies:
caniuse-lite: 1.0.30001680
caniuse-lite: 1.0.30001713
electron-to-chromium: 1.5.63
node-releases: 2.0.18
update-browserslist-db: 1.1.1(browserslist@4.24.2)
@@ -2266,7 +2266,7 @@ snapshots:
camelcase-css@2.0.1: {}
caniuse-lite@1.0.30001680: {}
caniuse-lite@1.0.30001713: {}
chokidar@3.6.0:
dependencies:

View File

@@ -8,7 +8,6 @@ const RefreshToken = async () => {
const token = localStorage.getItem("token")
const refreshAfter = localStorage.getItem("refreshAfter")
const currentTime = new Date().getTime();
// 1. 如果没有 token直接返回
if (!token) {
return;

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744433384929" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10449" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M624.64 593.92c153.6 0 276.48-122.88 276.48-276.48s-122.88-276.48-276.48-276.48-276.48 122.88-276.48 276.48c-10.24 153.6 122.88 276.48 276.48 276.48z m0-512c122.88 0 235.52 102.4 235.52 235.52 0 133.12-102.4 235.52-235.52 235.52-133.12 0-235.52-102.4-235.52-235.52 0-133.12 102.4-235.52 235.52-235.52z m378.88 665.6c20.48-20.48 30.72-71.68 0-102.4-20.48-40.96-61.44-61.44-102.4-51.2l-245.76 143.36c0-10.24-10.24-20.48-20.48-20.48h-10.24l-307.2-112.64c-10.24 0-20.48-10.24-40.96 0l-112.64 10.24c-20.48 0-30.72 20.48-30.72 40.96v225.28c0 30.72 10.24 40.96 40.96 40.96h92.16l307.2 61.44h10.24c10.24 0 20.48 0 30.72-10.24m-563.2-358.4h-30.72c-10.24 0-20.48 10.24-20.48 20.48v276.48c0 10.24 10.24 20.48 20.48 20.48h30.72c10.24 0 20.48-10.24 20.48-20.48v-276.48c0-10.24-10.24-20.48-20.48-20.48z m460.8-256v40.96h92.16v71.68h40.96v-71.68h81.92v-40.96h-81.92v-30.72h81.92v-40.96h-61.44l71.68-71.68-40.96-30.72-71.68 81.92-81.92-81.92-30.72 30.72 71.68 71.68h-71.68v40.96h81.92v30.72h-81.92z" fill="#4b64fa" p-id="10450"></path></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744433374959" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9288" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M932.8 443c-18.1-19.1-37.2-37.3-57.1-54.5-18.6-16.2-27.9-34.4-26.9-60.7 1.9-64-0.9-127.6 1.4-191.7 1.4-39.2-26-66.4-53.4-70.3-24.2-3.3-50.2-2.9-74.3 1.9-38.1 8.1-48.3 24.4-51.1 65-0.9 12.9-2.8 25.3-5.1 46.8-20.4-20.6-34.4-36.3-50.2-50.2-63.6-57.4-126.8-62.1-189.1-4.8C313.9 228.4 202.9 335 91.4 440.6c-22.8 22-35.8 70.3-18.1 94.2 13.5 18.2 38.1 29.6 59.5 39.7 14.9 7.2 20.9 14.8 20.9 31.5 0.9 83.6 3.3 167.8 4.6 251.4 0.9 63.1 35.8 100.4 96.6 100.8 164.9 0.5 329.9 0.5 494.3 0 66.9 0 104.1-40.1 103.6-110.4-0.5-66.4-1.4-132.4-1.9-198.8-0.5-55.4 0.5-52.6 50.6-73.6 62.4-26.7 80-79.8 31.3-132.4zM573.6 712.1c-22.8 35.8-43.2 72.6-46.5 117.1-1.4 21.5-16.3 30.1-33.9 25.3-29.3-7.6-59-16.2-85.5-30.6-33.4-18.2-33.4-23.9-20.4-61.7-55.8 16.7-55.8 16.7-82.7-36.8-16.3-32-16.3-33 10.2-55.4 34.8-29.6 69.7-59.7 103.6-90.3 13-11.5 25.6-12.9 39-2.9 36.7 27.2 73.4 54.5 109.6 82.7 23.8 18.2 23.4 26.3 6.6 52.6z m115.7-272.4c-28.3 33-56.2 66.9-85.9 98.9-11.6 12.9-15.8 22.5-7.9 41.1 9.8 24.4 17.7 52.1-3.3 81.3-48.3-37.8-94.8-74.1-143.1-111.4 20.9-21 45.1-30.1 65-23.4 43.2 14.8 61.3-14.8 83.2-40.6 20.9-24.4 41.8-48.7 63.2-72.6 8.8-10 20-20.1 33-8.1 14.4 12.8 4.6 24.3-4.2 34.8z m0 0" p-id="9289" fill="#4b64fa"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1745492151740" class="icon" viewBox="0 0 1304 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2946" xmlns:xlink="http://www.w3.org/1999/xlink" width="254.6875" height="200"><path d="M779.328526 51.733041a265.580712 265.580712 0 0 1 76.084603 134.059836c4.811397 22.836603 7.378411 46.192219 7.75715 69.603945a301.37863 301.37863 0 0 1-24.211287 119.934246 226.065534 226.065534 0 0 1-86.506959 105.822685l86.506959 42.278576 186.227726 103.900931c19.357808 9.019616 31.42137 30.158904 30.243068 52.939398v282.624a62.365808 62.365808 0 0 1-13.831013 40.58126 42.797589 42.797589 0 0 1-35.461261 16.734685H887.297403V850.200548h-57.975233l-0.070137 169.998027H771.290827V906.871233h-57.975232v113.313315h-57.975233v-84.963945h-57.919124l-0.056109 84.963945h-58.031343V906.871233h-57.93315v113.313315h-58.031343v-56.628603h-57.975233v56.628603h-79.689644a41.240548 41.240548 0 0 1-33.707835-16.734685 62.001096 62.001096 0 0 1-13.859069-40.58126V680.272658c-1.178301-22.794521 10.899288-43.989918 30.243069-52.939398l282.638027-138.422356a228.927123 228.927123 0 0 1-86.520986-102.02126 282.315397 282.315397 0 0 1-29.499617-126.106302 246.012493 246.012493 0 0 1 0-38.827835 368.499726 368.499726 0 0 1 24.281425-88.148165 221.029699 221.029699 0 0 1 60.542247-82.060274c74.822137-68.98674 184.754849-68.98674 259.520876 0zM300.194718 97.420274a132.979726 132.979726 0 0 1 45.827507 7.86937c14.476274 5.218192 28.377425 12.021479 41.535123 20.241534a366.53589 366.53589 0 0 0-10.436384 239.868493 1100.028493 1100.028493 0 0 0 42.41885 99.875069l-175.735233 93.240109a266.18389 266.18389 0 0 0-51.90137 51.115836h-0.070137a134.410521 134.410521 0 0 0-19.007123 70.684055v240.948602H38.387375a33.343123 33.343123 0 0 1-27.732164-13.269917 51.817205 51.817205 0 0 1-10.380274-33.553535V644.03989c-1.977863-18.712548 7.939507-36.513315 24.225315-43.204383l195.26137-111.93863a192.540055 192.540055 0 0 1-68.355507-74.990466 230.933041 230.933041 0 0 1-25.936657-109.399671 234.341699 234.341699 0 0 1 13.845041-80.19463 212.444932 212.444932 0 0 1 37.214685-66.097096 181.724932 181.724932 0 0 1 55.310027-44.102137c21.041096-11.081644 44.508932-16.804822 68.313425-16.664548l0.042082-0.028055z m701.468055-1.08011c23.650192 0 46.963726 5.723178 68.355506 16.720658a177.755178 177.755178 0 0 1 55.366137 44.102137 214.366685 214.366685 0 0 1 37.158576 66.153205c9.258082 25.501808 13.943233 52.757041 13.831013 80.194631a226.542466 226.542466 0 0 1-25.92263 108.43178c-15.710685 30.621808-38.350904 56.460274-65.746411 74.990466l200.465535 118.012493a35.671671 35.671671 0 0 1 18.936986 39.276713v230.189589a51.761096 51.761096 0 0 1-10.436384 33.497424 33.385205 33.385205 0 0 1-27.676054 13.326028h-138.730959V680.272658a135.434521 135.434521 0 0 0-19.077261-70.670028 119.232877 119.232877 0 0 0-50.709041-47.623014l-175.861479-97.770958s31.323178-68.593973 40.623342-99.875069a355.636603 355.636603 0 0 0 14.532384-103.157479 364.010959 364.010959 0 0 0-6.073863-68.818411 348.53874 348.53874 0 0 0-19.007123-64.329644A187.181589 187.181589 0 0 1 954.067814 105.065205a134.242192 134.242192 0 0 1 47.552876-8.78115l0.042083 0.056109zM849.844252 590.048438l-2.833534 2.833535-125.545206 157.906411a13.845041 13.845041 0 0 1-16.229698 4.36252l-3.226302-1.921753-154.581917-119.429261a13.648658 13.648658 0 0 0-16.566357-0.280548l-2.735342 2.538959L380.529622 815.973699a16.089425 16.089425 0 0 0-1.122192 18.474082l2.482849 3.072 26.203179 24.365589a13.746849 13.746849 0 0 0 17.365917 1.248438l2.945754-2.665205 109.385644-133.316384a13.788932 13.788932 0 0 1 16.117479-4.124055l3.170192 1.865644 155.283288 120.158685a13.887123 13.887123 0 0 0 16.790794 0.112219l2.721315-2.665205 163.657644-205.922192c4.180164-5.400548 4.544877-12.821041 0.911781-18.600329l-2.552987-3.114082-26.609972-23.790466a13.718795 13.718795 0 0 0-17.408-0.96789v-0.05611h-0.028055z" fill="#4b64fa" p-id="2947"></path></svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744433362221" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8205" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M395.432236 766.334056c0-188.806941 155.488069-339.852495 344.295011-339.852494 73.301518 0 142.160521 22.212581 202.13449 64.416486V57.752711c0-31.097614-26.655098-57.752711-57.752711-57.752711H80.013581C48.915967 0 22.26087 24.433839 22.26087 57.752711v908.494578C22.26087 997.344902 48.915967 1021.778742 80.013581 1021.778742h433.145336c-73.301518-62.195228-117.726681-153.266811-117.726681-255.444686z m428.70282-439.80911c-4.442516 8.885033-15.548807 13.327549-26.655097 13.327549H539.814015c-15.548807 0-28.876356-13.327549-28.876356-28.876356s13.327549-28.876356 28.876356-28.876356h257.665944c11.106291 0 19.991323 4.442516 26.655097 13.327549 4.442516 11.106291 4.442516 22.212581 0 31.097614zM513.158917 155.488069c4.442516-8.885033 15.548807-13.327549 26.655098-13.327548h257.665944c11.106291 0 19.991323 4.442516 26.655097 13.327548 4.442516 8.885033 4.442516 19.991323 0 28.876356s-15.548807 13.327549-26.655097 13.327549H539.814015c-11.106291 0-19.991323-4.442516-26.655098-13.327549-4.442516-8.885033-4.442516-19.991323 0-28.876356zM279.926813 113.284165c46.646421 0 86.629067 37.761388 86.629067 84.407809 0 46.646421-37.761388 84.407809-86.629067 84.407809-46.646421 0-86.629067-37.761388-86.629067-84.407809 2.221258-46.646421 39.982646-84.407809 86.629067-84.407809zM108.889937 397.605206c0-46.646421 37.761388-84.407809 86.629067-84.407809h173.258135c22.212581 0 44.425163 8.885033 59.973969 24.433839 15.548807 15.548807 24.433839 37.761388 24.43384 59.97397v57.752712H108.889937v-57.752712z" p-id="8206" fill="#4b64fa"></path><path d="M959.631802 764.112798c0-22.212581 15.548807-42.203905 39.982647-48.867679-11.106291-57.752711-44.425163-106.62039-86.629068-144.381778-15.548807 15.548807-44.425163 19.991323-62.195227 6.663774-19.991323-11.106291-31.097614-33.318872-24.43384-57.752712-26.655098-6.663774-59.97397-15.548807-86.629067-15.548806-31.097614 0-59.97397 4.442516-86.629067 15.548806 8.885033 22.212581-4.442516 46.646421-24.43384 57.752712-19.991323 11.106291-46.646421 6.663774-62.195227-6.663774-44.425163 37.761388-75.522777 86.629067-86.629068 144.381778 24.433839 4.442516 39.982646 26.655098 39.982647 48.867679s-15.548807 42.203905-39.982647 48.867679c11.106291 57.752711 44.425163 106.62039 86.629068 144.381779 15.548807-15.548807 44.425163-19.991323 62.195227-6.663774 19.991323 11.106291 31.097614 33.318872 24.43384 57.752711 26.655098 6.663774 59.97397 15.548807 86.629067 15.548807 31.097614 0 59.97397-4.442516 86.629067-15.548807-8.885033-22.212581 4.442516-46.646421 24.43384-57.752711 19.991323-11.106291 46.646421-6.663774 62.195227 6.663774 44.425163-37.761388 75.522777-86.629067 86.629068-144.381779-24.433839-6.663774-39.982646-26.655098-39.982647-48.867679z m-224.347071 106.620391c-62.195228 0-115.505423-48.867679-115.505423-111.062907 0-59.97397 51.088937-111.062907 115.505423-111.062907 62.195228 0 115.505423 48.867679 115.505423 111.062907 0 59.97397-53.310195 111.062907-115.505423 111.062907z" p-id="8207" fill="#4b64fa"></path></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1744433306868" class="icon" viewBox="0 0 1042 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1499" xmlns:xlink="http://www.w3.org/1999/xlink" width="203.515625" height="200"><path d="M656.457143 528.457143h-87.771429v69.485714h87.771429v-69.485714zM656.457143 398.628571h-87.771429v65.828572h87.771429v-65.828572z" fill="#4b64fa" p-id="1500"></path><path d="M901.485714 877.714286V237.714286c0-20.114286-16.457143-36.571429-36.571428-36.571429h-310.857143V109.714286c0-20.114286-16.457143-36.571429-36.571429-36.571429h-347.428571c-20.114286 0-36.571429 16.457143-36.571429 36.571429v768h-54.857143v73.142857h877.714286v-73.142857h-54.857143zM440.685714 667.428571c-7.314286-14.628571-18.285714-36.571429-29.257143-54.857142v204.8c0 1.828571-1.828571 3.657143-3.657142 3.657142h-62.171429c-1.828571 0-3.657143-1.828571-3.657143-3.657142V640c-18.285714 40.228571-34.742857 69.485714-51.2 91.428571 0 1.828571-1.828571 1.828571-3.657143 1.828572s-1.828571-1.828571-3.657143-1.828572c-5.485714-18.285714-20.114286-45.714286-29.257142-60.342857v-3.657143c27.428571-31.085714 56.685714-85.942857 76.8-140.8h-67.657143c-1.828571 0-3.657143-1.828571-3.657143-3.657142v-60.342858c0-1.828571 1.828571-3.657143 3.657143-3.657142h78.628571v-54.857143c-16.457143 3.657143-34.742857 5.485714-56.685714 7.314285-1.828571 0-3.657143-1.828571-3.657143-3.657142-1.828571-14.628571-10.971429-36.571429-16.457143-49.371429v-3.657143c0-1.828571 1.828571-1.828571 1.828572-1.828571 67.657143-10.971429 133.485714-27.428571 175.542857-43.885715 1.828571 0 3.657143 0 3.657143 1.828572l36.571428 53.028571v3.657143c0 1.828571-1.828571 1.828571-1.828571 1.828572-20.114286 7.314286-43.885714 14.628571-69.485715 20.114285V457.142857H475.428571c1.828571 0 3.657143 1.828571 3.657143 3.657143v60.342857c0 1.828571-1.828571 3.657143-3.657143 3.657143h-64v1.828571c16.457143 16.457143 64 73.142857 73.142858 85.942858 1.828571 1.828571 1.828571 3.657143 0 3.657142l-38.4 51.2c0 1.828571-1.828571 1.828571-3.657143 1.828572l-1.828572-1.828572z m334.628572 133.485715c0 1.828571-1.828571 3.657143-3.657143 3.657143H449.828571c-1.828571 0-3.657143-1.828571-3.657142-3.657143v-60.342857c0-1.828571 1.828571-3.657143 3.657142-3.657143h47.542858v-402.285715c0-1.828571 1.828571-3.657143 3.657142-3.657142h223.085715c1.828571 0 3.657143 1.828571 3.657143 3.657142v402.285715h43.885714c1.828571 0 3.657143 1.828571 3.657143 3.657143v60.342857z" fill="#4b64fa" p-id="1501"></path><path d="M656.457143 665.6h-87.771429v71.314286h87.771429v-71.314286z" fill="#4b64fa" p-id="1502"></path></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -2,6 +2,9 @@
<div class=" bg-gray-100 flex flex-col p-4">
<!-- 标题 -->
<div class="text-center text-2xl font-bold mb-4">授权书</div>
<div class="text-center text-lg font-bold text-red-500 mb-2">
(未成年人不得使用)
</div>
<!-- 授权书滚动区域 -->
<div class="card flex-1 overflow-y-auto" ref="agreementBox" @scroll="handleScroll">
@@ -9,7 +12,7 @@
<p class="indent-[2em]">
本人<span class="font-bold">
{{ signature ? props.name : "____________" }}</span>
拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告及查询可能与我本人有关的公开的法律信息因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
</p>
<p class="mt-2 font-bold">授权内容如下</p>
<ol class="list-decimal pl-6">
@@ -19,10 +22,16 @@
<li>
依法成立的第三方服务商查询或核实搜集保存处理共享使用含合法业务应用本人相关数据且不再另行告知本人但法律法规监管政策禁止的除外
</li>
<li>
本人授权贵司通过相关渠道查询可能与我本人有关的公开的法律信息用于协助我了解自身法律状况
</li>
<!-- <li>本人授权本业务推广方 可浏览本人大数据报告</li> -->
<li>
本人授权有效期为自授权之日起
1个月本授权为不可撤销授权但法律法规另有规定的除外
1个月本授权为不可撤销授权但法律法规另有规定的除外平台在我的授权下不构成任何对我本人的侵权行为
</li>
<li>
我点击"签署"则视为我本人同意授权本次查询
</li>
</ol>
<p class="mt-2 font-bold">用户声明与承诺</p>
@@ -36,6 +45,9 @@
<li>
若用户冒名签署或提供虚假信息由用户自行承担全部法律责任平台不承担任何后果
</li>
<li>
我声明我是完全民事行为能力人我完全理解并同意上述所有内容授权是我真实意思表示
</li>
</ul>
<p class="mt-2 font-bold">特别提示</p>
<ul class="list-decimal pl-6">
@@ -54,6 +66,12 @@
<li>
本授权书中涉及的数据查询和报告生成由依法成立的第三方服务商提供若因第三方行为导致数据错误或损失用户应向第三方主张权利平台不承担相关责任
</li>
<li>
已公开的司法数据未必有您本人(或者委托人)的信息请知悉!
</li>
<li>
本服务是虚拟数据服务一经提交查询则会产生后台信息服务
</li>
</ul>
<p class="mt-2 font-bold">附加说明</p>
<ul class="list-decimal pl-6">
@@ -64,7 +82,7 @@
本人有权随时撤回本授权书中的授权但撤回前的授权行为及其法律后果仍具有法律效力若需撤回授权本人可通过贵司官方渠道提交书面申请贵司将在收到申请后依法停止对本人数据的使用
</li>
<li>
你通过天远查自愿支付相应费用用于购买广西福铭网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
你通过"天远查"自愿支付相应费用用于购买广西福铭网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP"联系客服"按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
</li>
<li>
你向广西福铭网络科技有限公司的支付方式为广西福铭网络科技有限公司及其经官方授权的相关企业的支付宝账户
@@ -73,7 +91,7 @@
<p class="mt-2 font-bold">争议解决机制</p>
<ul>
<li>
若因本授权书引发争议双方应友好协商解决协商不成的双方同意将争议提交至授权书签署地海南省有管辖权的人民法院解决
若因本授权书引发争议双方应友好协商解决协商不成的双方同意将争议提交至授权书签署地广西壮族自治区有管辖权的人民法院解决
</li>
</ul>
<p class="mt-2 font-bold">签署方式的法律效力声明</p>

View File

@@ -0,0 +1,904 @@
<script setup>
const props = defineProps({
feature: {
type: String,
required: true,
},
reportData: {
type: Array,
required: true,
},
reportParams: {
type: Object,
required: true,
},
reportName: {
type: String,
required: true,
},
reportDateTime: {
type: String,
required: true,
},
isEmpty: {
type: Boolean,
required: true,
},
isDone: {
type: Boolean,
required: true,
},
});
// 使用toRefs将props转换为组件内的ref
const {
feature,
reportData,
reportParams,
reportName,
reportDateTime,
isEmpty,
isDone,
} = toRefs(props);
const active = ref(null);
const reportScore = ref(0); // 默认分数
watch(reportData, () => {
reportScore.value = calculateScore(reportData.value);
});
const featureMap = {
G09SC02: {
name: "婚姻状态",
component: defineAsyncComponent(() => import("@/ui/CMarriage.vue")),
remark: '查询结果为"未婚或尚未登记结婚"时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异、离异冷静期。如您对查询结果有疑问,请联系客服反馈。',
},
G27BJ05: {
name: "借贷申请记录",
component: defineAsyncComponent(() =>
import("@/ui/CBankLoanApplication.vue")
),
},
G28BJ05: {
name: "借贷行为记录",
component: defineAsyncComponent(() =>
import("@/ui/CBankLoanBehavior.vue")
),
},
G26BJ05: {
name: "借贷违约失信",
component: defineAsyncComponent(() => import("@/ui/CSpecialList.vue")),
},
G34BJ03: {
name: "不良记录",
component: defineAsyncComponent(() => import("@/ui/CBad.vue")),
},
G35SC01: {
name: "司法涉诉",
component: defineAsyncComponent(() => import("@/ui/CLawsuit.vue")),
},
G22SC01: {
name: "司法涉诉",
component: defineAsyncComponent(() => import("@/ui/CLawsuitPP.vue")),
},
G05HZ01: {
name: "股东人企关系",
component: defineAsyncComponent(() =>
import("@/ui/CPersonEnterprisePro/index.vue")
),
},
PersonEnterprisePro: {
name: "人企关系加强版",
component: defineAsyncComponent(() =>
import("@/ui/CPersonEnterprisePro/index.vue")
),
},
Q23SC01: {
name: "企业涉诉",
component: defineAsyncComponent(() => import("@/ui/CLawsuit.vue")),
},
G22SC01: {
name: "司法涉诉",
component: defineAsyncComponent(() => import("@/ui/CLawsuitPP.vue")),
},
G15BJ02: {
name: "手机三要素",
component: defineAsyncComponent(() =>
import("@/ui/CPhoneThreeElements.vue")
),
},
KZEYS: {
name: "身份证二要素",
component: defineAsyncComponent(() =>
import("@/ui/CIDCardTwoElements.vue")
),
},
G17BJ02: {
name: "手机号二要素",
component: defineAsyncComponent(() =>
import("@/ui/CPhoneTwoElements.vue")
),
},
G10SC02: {
name: "双人婚姻核验",
component: defineAsyncComponent(() => import("@/ui/CDualMarriage.vue")),
},
P_C_B332: {
name: "人车核验",
component: defineAsyncComponent(() => import("@/ui/CP_C_B332.vue")),
},
FIN019: {
name: "银行卡黑名单",
component: defineAsyncComponent(() => import("@/ui/CFIN019.vue")),
},
G20GZ01: {
name: "银行卡四要素核验",
component: defineAsyncComponent(() => import("@/ui/CG20GZ01.vue")),
},
G03HZ01: {
name: "手机号码风险",
component: defineAsyncComponent(() => import("@/ui/CG03HZ01.vue")),
},
G19BJ02: {
name: "手机二次卡",
component: defineAsyncComponent(() => import("@/ui/CG19BJ02.vue")),
},
G02BJ02: {
name: "手机在网时长",
component: defineAsyncComponent(() => import("@/ui/CG02BJ02.vue")),
},
CAR061: {
name: "名下车辆",
component: defineAsyncComponent(() => import("@/ui/CCAR061.vue")),
},
IDV044: {
name: "婚姻状态",
component: defineAsyncComponent(() => import("@/ui/CIDV044.vue")),
remark: '查询结果为"未婚或尚未登记结婚"时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异、离异冷静期。如您对查询结果有疑问,请联系客服反馈。',
},
BehaviorRiskScan: {
name: "风险行为扫描",
component: defineAsyncComponent(() =>
import("@/ui/CBehaviorRiskScan.vue")
),
},
};
const maskValue = computed(() => {
return (type, value) => {
if (!value) return value;
if (type === "name") {
// 姓名脱敏(保留首位)
if (value.length === 1) {
return "*"; // 只保留一个字,返回 "*"
} else if (value.length === 2) {
return value[0] + "*"; // 两个字,保留姓氏,第二个字用 "*" 替代
} else {
return (
value[0] +
"*".repeat(value.length - 2) +
value[value.length - 1]
); // 两个字以上,保留第一个和最后一个字,其余的用 "*" 替代
}
} else if (type === "id_card") {
// 身份证号脱敏保留前6位和最后4位
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "mobile") {
if (value.length === 11) {
return value.substring(0, 3) + "****" + value.substring(7);
}
return value; // 如果手机号不合法或长度不为 11 位,直接返回原手机号
} else if (type === "bank_card") {
// 银行卡号脱敏保留前6位和后4位
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "ent_name") {
// 企业名称脱敏保留前3个字符和后3个字符中间部分用 "*" 替代)
if (value.length <= 6) {
return value[0] + "*".repeat(value.length - 1); // 少于6个字符时只保留第一个字符其他用 * 替代
} else {
return (
value.slice(0, 3) +
"*".repeat(value.length - 6) +
value.slice(-3)
); // 多于6个字符时保留前3和后3
}
} else if (type === "ent_code") {
// 企业代码脱敏保留前4个字符和后4个字符中间部分用 "*" 替代)
if (value.length <= 8) {
return value.slice(0, 4) + "*".repeat(value.length - 4); // 长度不超过8时保留前4个字符其他用 * 替代
} else {
return (
value.slice(0, 4) +
"*".repeat(value.length - 8) +
value.slice(-4)
); // 长度超过8时保留前4个字符和后4个字符
}
} else if (type === "car_license") {
// 车牌号脱敏保留前2个字符后2个字符其他部分用 "*" 替代)
if (value.length <= 4) {
return value[0] + "*".repeat(value.length - 1); // 如果车牌号长度小于等于4只保留首字符
} else {
// 如果车牌号较长保留前2个字符后2个字符其余部分用 "*" 替代
return (
value.slice(0, 2) +
"*".repeat(value.length - 4) +
value.slice(-2)
);
}
}
return value;
};
});
// 计算综合评分的函数
const calculateScore = (reportData) => {
// 从0分开始0分表示无风险
let score = 0;
// 最高分为90分90分表示最高风险
const maxScore = 90;
// 定义各接口的相对风险权重比例
const relativeWeights = {
// 关键风险指标(高优先级)
G34BJ03: 250, // 不良记录
G26BJ05: 100, // 违约异常
G22SC01: 400, // 司法涉诉
G35SC01: 20, // 司法涉诉(次要)
Q23SC01: 50, // 企业涉诉
FIN019: 100, // 银行卡黑名单
// 高风险指标(中优先级)
G27BJ05: 40, // 借贷申请记录
G28BJ05: 40, // 借贷行为记录
G03HZ01: 70, // 手机号码风险
// 中风险指标(低优先级)
G19BJ02: 50, // 手机二次卡
G02BJ02: 50, // 手机在网时长
G05HZ01: 50, // 人企关系
// 验证指标(最低优先级)
G15BJ02: 25, // 手机三要素
G17BJ02: 25, // 手机号二要素
KZEYS: 25, // 身份证二要素
G20GZ01: 25, // 银行卡四要素核验
};
// 找出当前报告中包含的接口
const availableAPIs = reportData
.map((item) => item.data.apiID)
.filter((id) => relativeWeights[id]);
// 如果没有可评分的接口,返回默认分数
if (availableAPIs.length === 0) return 30; // 默认30分中等风险
// 计算当前报告中所有接口的相对权重总和
let totalWeight = 0;
availableAPIs.forEach((apiID) => {
totalWeight += relativeWeights[apiID];
});
// 计算每个权重点对应的分数
const pointValue = maxScore / totalWeight;
// 基于当前报告中的接口计算实际权重
const actualWeights = {};
availableAPIs.forEach((apiID) => {
// 将相对权重转换为实际分数权重
actualWeights[apiID] = relativeWeights[apiID] * pointValue;
});
// 遍历报告数据进行评分 - 风险越高分数越高
reportData.forEach((item) => {
const apiID = item.data.apiID;
const data = item.data.data;
// 如果没有定义权重,跳过
if (!actualWeights[apiID]) return;
// 根据不同的API ID计算分数有风险时加分
switch (apiID) {
case "G09SC02": // 婚姻状态
// 不计入风险
break;
case "G27BJ05": // 借贷申请记录
if (data) {
// 检查是否有申请记录(有则表示风险)
let hasRisk = false;
for (const key in data) {
if (
data[key] !== 0 &&
data[key] !== "0" &&
key.indexOf("allnum") > -1 &&
!isNaN(parseInt(data[key])) &&
parseInt(data[key]) > 0
) {
hasRisk = true;
break;
}
}
if (hasRisk) {
score += actualWeights[apiID];
}
}
break;
case "G28BJ05": // 借贷行为记录
if (data) {
// 检查是否有借贷记录(有则表示风险)
let hasRisk = false;
for (const key in data) {
if (
data[key] !== 0 &&
data[key] !== "0" &&
(key.indexOf("lendamt") > -1 ||
key.indexOf("num") > -1) &&
!isNaN(parseInt(data[key])) &&
parseInt(data[key]) > 0
) {
hasRisk = true;
break;
}
}
if (hasRisk) {
score += actualWeights[apiID];
}
}
break;
case "G26BJ05": // 违约异常
if (data) {
// 检查除特定字段外的其他字段是否有异常值(有异常则表示风险)
const excludeFields = [
"swift_number",
"code",
"flag_specialList_c",
];
let hasRisk = false;
for (const key in data) {
if (
!excludeFields.includes(key) &&
data[key] !== 0 &&
data[key] !== "0"
) {
hasRisk = true;
break;
}
}
if (hasRisk) {
score += actualWeights[apiID];
}
}
break;
case "G34BJ03": // 不良记录
if (data && data.risk_level) {
// 根据风险等级加分
switch (data.risk_level) {
case "A": // 无风险
// 不加分
break;
case "F": // 低风险
score += actualWeights[apiID] * 0.3;
break;
case "C": // 中风险
case "D": // 中风险
score += actualWeights[apiID] * 0.7;
break;
case "B": // 高风险
case "E": // 高风险
score += actualWeights[apiID];
break;
}
}
break;
case "G35SC01": // 司法涉诉
case "G22SC01": // 司法涉诉
case "Q23SC01": // 企业涉诉
if (data) {
let hasRisk = false;
// 检查各种涉诉信息 - 处理嵌套数据结构
// entout是一个单元素数组数组中第一个元素是JSON对象对象中有entout属性
if (
data.entout &&
Array.isArray(data.entout) &&
data.entout.length > 0 &&
data.entout[0] &&
data.entout[0].entout &&
((Array.isArray(data.entout[0].entout) &&
data.entout[0].entout.length > 0) ||
(typeof data.entout[0].entout === "object" &&
Object.keys(data.entout[0].entout).length > 0))
) {
hasRisk = true;
}
// 处理sxbzxr(失信被执行人)嵌套结构
if (
data.sxbzxr &&
Array.isArray(data.sxbzxr) &&
data.sxbzxr.length > 0 &&
data.sxbzxr[0] &&
data.sxbzxr[0].sxbzxr &&
((Array.isArray(data.sxbzxr[0].sxbzxr) &&
data.sxbzxr[0].sxbzxr.length > 0) ||
(typeof data.sxbzxr[0].sxbzxr === "object" &&
Object.keys(data.sxbzxr[0].sxbzxr).length > 0))
) {
hasRisk = true;
}
// 处理xgbzxr(限制高消费被执行人)嵌套结构
if (
data.xgbzxr &&
Array.isArray(data.xgbzxr) &&
data.xgbzxr.length > 0 &&
data.xgbzxr[0] &&
data.xgbzxr[0].xgbzxr &&
((Array.isArray(data.xgbzxr[0].xgbzxr) &&
data.xgbzxr[0].xgbzxr.length > 0) ||
(typeof data.xgbzxr[0].xgbzxr === "object" &&
Object.keys(data.xgbzxr[0].xgbzxr).length > 0))
) {
hasRisk = true;
}
if (hasRisk) {
score += actualWeights[apiID];
}
}
break;
case "G03HZ01": // 手机号码风险
if (data && data.filterType) {
// 根据filterType判断风险等级
switch (data.filterType) {
case "0": // 安全
// 不加分
break;
case "3": // 低危
score += actualWeights[apiID] * 0.3;
break;
case "2": // 中危
score += actualWeights[apiID] * 0.7;
break;
case "1": // 高危
score += actualWeights[apiID];
break;
}
}
break;
case "G19BJ02": // 手机二次卡
if (data && data.is_second_card === true) {
score += actualWeights[apiID];
}
break;
case "G02BJ02": // 手机在网时长
if (data && data.online_months < 6) {
score += actualWeights[apiID];
}
break;
case "G15BJ02": // 手机三要素
case "G17BJ02": // 手机号二要素
case "KZEYS": // 身份证二要素
case "G20GZ01": // 银行卡四要素核验
if (data && data.is_consistent === false) {
score += actualWeights[apiID];
}
break;
case "FIN019": // 银行卡黑名单
if (data && data.is_blacklisted === true) {
score += actualWeights[apiID];
}
break;
default:
// 未知接口类型不影响评分
break;
}
});
// 确保分数在0-90范围内并四舍五入
return Math.max(0, Math.min(maxScore, Math.round(score)));
};
</script>
<template>
<div class="min-h-full from-blue-100 to-white bg-gradient-to-b">
<template v-if="isDone">
<van-tabs
v-model:active="active"
shrink
scrollspy
sticky
:offset-top="46"
>
<div class="flex flex-col gap-y-4 p-4">
<LEmpty v-if="isEmpty" />
<van-tab title="分析指数">
<div id="overdiv" class="title mb-4">分析指数</div>
<div class="card mb-4">
<div class="my-4">
<GaugeChart :score="reportScore" />
</div>
</div>
</van-tab>
<van-tab title="基本信息">
<div id="overdiv" class="title mb-4">基本信息</div>
<div class="card">
<div class="flex flex-col gap-y-2">
<LTitle
title="报告信息"
type="blue-green"
></LTitle>
<div class="flex flex-col gap-2 my-2">
<div
class="flex justify-between border-b pb-2 pl-2"
>
<span class="text-gray-700 font-bold"
>报告时间</span
>
<span class="text-gray-600"
>2025-1-1 12:00:00</span
>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="!isEmpty"
>
<span class="text-gray-700 font-bold"
>报告项目</span
>
<span class="text-gray-600">
{{ reportName }}</span
>
</div>
</div>
<template
v-if="Object.keys(reportParams).length != 0"
>
<LTitle
title="报告对象"
type="blue-green"
></LTitle>
<div class="flex flex-col gap-2 my-2">
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.name"
>
<span
class="text-gray-700 font-bold"
>姓名</span
>
<span class="text-gray-600">{{
maskValue(
"name",
reportParams?.name
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.id_card"
>
<span
class="text-gray-700 font-bold"
>身份证号</span
>
<span class="text-gray-600">
{{
maskValue(
"id_card",
reportParams?.id_card
)
}}</span
>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.nameMan"
>
<span
class="text-gray-700 font-bold"
>男方姓名</span
>
<span class="text-gray-600">{{
maskValue(
"name",
reportParams?.nameMan
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.idCardMan"
>
<span
class="text-gray-700 font-bold"
>男方身份证号</span
>
<span class="text-gray-600">{{
maskValue(
"id_card",
reportParams?.idCardMan
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.nameWoman"
>
<span
class="text-gray-700 font-bold"
>女方姓名</span
>
<span class="text-gray-600">{{
maskValue(
"name",
reportParams?.nameWoman
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.idCardWoman"
>
<span
class="text-gray-700 font-bold"
>女方身份证号</span
>
<span class="text-gray-600">{{
maskValue(
"id_card",
reportParams?.idCardWoman
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.bank_card"
>
<span
class="text-gray-700 font-bold"
>银行卡号</span
>
<span class="text-gray-600">{{
maskValue(
"bank_card",
reportParams?.bank_card
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.mobile"
>
<span
class="text-gray-700 font-bold"
>手机号</span
>
<span class="text-gray-600">{{
maskValue(
"mobile",
reportParams?.mobile
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="
reportParams?.verification_code
"
>
<span
class="text-gray-700 font-bold"
>验证码</span
>
<span class="text-gray-600">{{
maskValue(
"code",
reportParams?.verification_code
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.car_license"
>
<span
class="text-gray-700 font-bold"
>车牌号</span
>
<span class="text-gray-600">{{
maskValue(
"car_license",
reportParams?.car_license
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.ent_name"
>
<span
class="text-gray-700 font-bold"
>企业名称</span
>
<span class="text-gray-600">{{
maskValue(
"ent_name",
reportParams?.ent_name
)
}}</span>
</div>
<div
class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.ent_code"
>
<span
class="text-gray-700 font-bold"
>企业代码</span
>
<span class="text-gray-600">{{
maskValue(
"ent_code",
reportParams?.ent_code
)
}}</span>
</div>
<div class="flex flex-col gap-4">
<div
class="flex items-center bg-blue-100 rounded-xl px-4 py-2 flex-1"
>
<div
class="bg-green-500 w-12 h-12 text-white text-xl flex items-center justify-center rounded-full mr-4"
>
</div>
<div>
<div
class="font-bold text-lg"
>
身份证检查结果
</div>
<div
class="text-sm text-gray-600"
>
身份证信息核验通过
</div>
</div>
</div>
<div
class="flex items-center bg-blue-100 rounded-xl px-4 py-2 flex-1"
>
<div
class="bg-green-500 w-12 h-12 text-white text-xl flex items-center justify-center rounded-full mr-4"
>
</div>
<div>
<div
class="font-bold text-lg"
>
手机号检测结果
</div>
<div
class="text-sm text-gray-600"
>
被查询人姓名与运营商提供的一致
</div>
<div
class="text-sm text-gray-600"
>
被查询人身份证与运营商提供的一致
</div>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<LRemark
content="如查询的姓名/身份证与运营商提供的不一致,可能会存在报告内容不匹配的情况"
/>
</van-tab>
<van-tab
v-for="(item, index) in reportData"
:key="index"
:title="featureMap[item.data.apiID]?.name"
>
<div id="lawsuit" class="title mb-4">
{{ featureMap[item.data.apiID]?.name }}
</div>
<component
:is="featureMap[item.data.apiID]?.component"
:data="item.data.data"
:params="reportParams"
>
</component>
<LRemark
v-if="featureMap[item.data.apiID]?.remark"
:content="featureMap[item.data.apiID]?.remark"
/>
</van-tab>
<div class="card">
<div>
<div class="text-bold text-blue-500 mb-2">
报告说明
</div>
<div>
&nbsp;
&nbsp;本报告的数据由用户本人明确授权后,我们才向相关合法存有用户个人数据的机构调取本报告相关内容,本平台只做大数据的获取与分析,仅向用户个人展示参考。
</div>
<p>
&nbsp; &nbsp; 报告有效期<strong
class="text-red-500"
>15天</strong
>,过期自动删除。
</p>
<p>
&nbsp; &nbsp;
若您的数据不全面,可能是数据具有延迟性或者合作信息机构未获取到您的数据。若数据有错误请联系客服
</p>
<p>
&nbsp;
&nbsp;本产品所有数据均来自第三方。可能部分数据未公开、数据更新延迟或信息受到限制,贵司不对数据的准确性、真实性、完整性做任何承诺。用户需根据实际情况,结合报告内容自行判断与决策。
</p>
</div>
</div>
</div>
</van-tabs>
</template>
</div>
<div class="disclaimer">
<div class="flex flex-col items-center">
<div class="flex items-center">
<img
class="w-4 h-4 mr-2"
src="@/assets/images/public_security_record_icon.png"
alt="公安备案"
/>
<text>琼公网安备46010002000443号</text>
</div>
<div>
<a class="text-blue-500" href="https://beian.miit.gov.cn">
琼ICP备2024038584号-2
</a>
</div>
</div>
<div>海南省学宇思科技有限公司版权所有</div>
</div>
</template>
<style lang="scss" scoped>
.title {
@apply mx-auto mt-2 w-64 border rounded-3xl bg-gradient-to-r from-blue-400 via-green-500 to-teal-500 py-2 text-center text-white font-bold;
}
.a {
color: #e03131;
}
.disclaimer {
/* margin-top: 24px; */
padding: 10px;
font-size: 12px;
color: #999;
text-align: center;
border-top: 1px solid #e0e0e0;
padding-bottom: 60px;
background: #ffffff;
}
:deep(.card) {
@apply p-3;
}
</style>

View File

@@ -0,0 +1,255 @@
<template>
<div>
<div ref="chartRef" :style="{ width: '100%', height: '200px' }"></div>
<div class="risk-description">
{{ riskDescription }}
</div>
</div>
</template>
<script setup>
import * as echarts from "echarts";
import { ref, onMounted, onUnmounted, watch, computed } from "vue";
const props = defineProps({
score: {
type: Number,
required: true,
},
});
// 根据分数计算风险等级和颜色
const riskLevel = computed(() => {
const score = props.score;
if (score >= 0 && score <= 25) {
return {
level: "无任何风险",
color: "#52c41a",
gradient: [
{ offset: 0, color: "#52c41a" },
{ offset: 1, color: "#7fdb42" }
]
};
} else if (score > 25 && score <= 50) {
return {
level: "风险指数较低",
color: "#faad14",
gradient: [
{ offset: 0, color: "#faad14" },
{ offset: 1, color: "#ffc53d" }
]
};
} else if (score > 50 && score <= 75) {
return {
level: "风险指数较高",
color: "#fa8c16",
gradient: [
{ offset: 0, color: "#fa8c16" },
{ offset: 1, color: "#ffa940" }
]
};
} else {
return {
level: "高风险警告",
color: "#f5222d",
gradient: [
{ offset: 0, color: "#f5222d" },
{ offset: 1, color: "#ff4d4f" }
]
};
}
});
// 评分解释文本
const riskDescription = computed(() => {
const score = props.score;
if (score >= 0 && score <= 25) {
return "根据综合分析,当前报告未检测到明显风险因素,各项指标表现正常,总体状况良好。";
} else if (score > 25 && score <= 50) {
return "根据综合分析,当前报告存在少量风险信号,建议关注相关指标变化,保持警惕。";
} else if (score > 50 && score <= 75) {
return "根据综合分析,当前报告风险指数较高,多项指标显示异常,建议进一步核实相关情况。";
} else {
return "根据综合分析,当前报告显示高度风险状态,多项重要指标严重异常,请立即采取相应措施。";
}
});
const chartRef = ref(null);
let chartInstance = null;
const initChart = () => {
if (!chartRef.value) return;
// 初始化ECharts实例
chartInstance = echarts.init(chartRef.value);
updateChart();
};
const updateChart = () => {
if (!chartInstance) return;
// 获取当前风险等级信息
const risk = riskLevel.value;
// 配置项
const option = {
series: [
{
type: "gauge",
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
radius: "100%",
center: ["50%", "80%"],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, risk.gradient),
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.2)',
},
progress: {
show: true,
width: 20,
roundCap: true,
clip: false
},
axisLine: {
roundCap: true,
lineStyle: {
width: 20,
color: [
[1, new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: "rgba(0, 0, 0, 0.1)"
},
{
offset: 1,
color: "rgba(0, 0, 0, 0.05)"
}
])]
]
}
},
axisTick: {
show: false
},
splitLine: {
show: true,
distance: -26,
length: 5,
lineStyle: {
color: "#999",
width: 2
}
},
axisLabel: {
show: true,
distance: -8,
fontSize: 12,
color: "#999",
formatter: function (value) {
if (value % 20 === 0) {
return value;
}
return "";
}
},
anchor: {
show: false
},
pointer: {
icon: "triangle",
iconStyle: {
color: risk.color,
borderColor: risk.color,
borderWidth: 1
},
offsetCenter: ["7%", "-67%"],
length: "10%",
width: 15
},
detail: {
valueAnimation: true,
fontSize: 30,
fontWeight: "bold",
color: risk.color,
offsetCenter: [0, "-25%"],
formatter: function (value) {
return `{value|${value}分}\n{level|${risk.level}}`;
},
rich: {
value: {
fontSize: 30,
fontWeight: 'bold',
color: risk.color,
padding: [0, 0, 5, 0]
},
level: {
fontSize: 14,
fontWeight: 'normal',
color: risk.color,
padding: [5, 0, 0, 0]
}
}
},
data: [
{
value: props.score
}
],
title: {
fontSize: 14,
color: risk.color,
offsetCenter: [0, "10%"],
formatter: risk.level
}
}
]
};
// 使用配置项设置图表
chartInstance.setOption(option);
};
// 监听分数变化
watch(
() => props.score,
() => {
updateChart();
}
);
onMounted(() => {
initChart();
// 处理窗口大小变化
window.addEventListener("resize", () => {
if (chartInstance) {
chartInstance.resize();
}
});
});
// 在组件销毁前清理
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener("resize", chartInstance?.resize);
});
</script>
<style scoped>
.risk-description {
margin-top: 10px;
padding: 8px 12px;
background-color: #f5f5f5;
border-radius: 4px;
color: #666;
font-size: 12px;
line-height: 1.5;
text-align: center;
}
</style>

View File

@@ -1,87 +1,95 @@
<script setup>
// 接收 type 和 options props 以及 v-model
const props = defineProps({
type: {
type: String,
default: 'purple-pink', // 默认颜色渐变
},
options: {
type: Array,
required: true, // 动态传入选项
},
modelValue: {
type: String,
default: '', // v-model 绑定的值
},
})
const emit = defineEmits(['update:modelValue'])
type: {
type: String,
default: "purple-pink", // 默认颜色渐变
},
options: {
type: Array,
required: true, // 动态传入选项
},
modelValue: {
type: String,
default: "", // v-model 绑定的值
},
});
const emit = defineEmits(["update:modelValue"]);
// 选中内容绑定 v-model
const selected = ref(props.modelValue)
const selected = ref(props.modelValue);
// 监听 v-model 的变化
watch(() => props.modelValue, (newValue) => {
selected.value = newValue
})
watch(
() => props.modelValue,
(newValue) => {
selected.value = newValue;
}
);
// 根据type动态生成分割线的类名
const lineClass = computed(() => {
switch (props.type) {
case 'blue-green':
return 'bg-gradient-to-r from-blue-400 via-green-500 to-teal-500'
case 'orange-yellow':
return 'bg-gradient-to-r from-orange-400 via-yellow-500 to-yellow-600'
case 'red-purple':
return 'bg-gradient-to-r from-red-500 via-purple-500 to-purple-600'
default:
return 'bg-gradient-to-r from-purple-400 via-pink-500 to-red-500'
}
})
switch (props.type) {
case "blue-green":
return "bg-gradient-to-r from-blue-400 via-green-500 to-teal-500";
case "orange-yellow":
return "bg-gradient-to-r from-orange-400 via-yellow-500 to-yellow-600";
case "red-purple":
return "bg-gradient-to-r from-red-500 via-purple-500 to-purple-600";
default:
return "bg-gradient-to-r from-purple-400 via-pink-500 to-red-500";
}
});
// 计算滑动线的位置和宽度
const slideLineStyle = computed(() => {
const index = props.options.findIndex(option => option.value === selected.value)
const buttonWidth = 100 / props.options.length
return {
width: `${buttonWidth}%`,
transform: `translateX(${index * 100}%)`,
}
})
const index = props.options.findIndex(
(option) => option.value === selected.value
);
const buttonWidth = 100 / props.options.length;
return {
width: `${buttonWidth}%`,
transform: `translateX(${index * 100}%)`,
};
});
// 选择选项函数
function selectOption(option) {
selected.value = option.value
// 触发 v-model 的更新
emit('update:modelValue', option.value)
selected.value = option.value;
// 触发 v-model 的更新
emit("update:modelValue", option.value);
}
</script>
<template>
<div class="relative flex">
<div
v-for="(option, index) in options"
:key="index"
class="flex-1 shrink-0 cursor-pointer py-2 text-center text-size-sm font-bold transition-transform duration-200 ease-in-out"
:class="{ 'text-gray-900': selected === option.value, 'text-gray-500': selected !== option.value }"
@click="selectOption(option)"
>
{{ option.label }}
<div class="relative flex">
<div
v-for="(option, index) in options"
:key="index"
class="mx-4 flex-shrink-0 cursor-pointer py-2 text-center text-size-sm font-bold transition-transform duration-200 ease-in-out"
:class="{
'text-gray-900': selected === option.value,
'text-gray-500': selected !== option.value,
}"
@click="selectOption(option)"
>
{{ option.label }}
</div>
<div
class="absolute bottom-0 h-[3px] rounded transition-all duration-300"
:style="slideLineStyle"
:class="lineClass"
/>
</div>
<div
class="absolute bottom-0 h-[3px] rounded transition-all duration-300"
:style="slideLineStyle"
:class="lineClass"
/>
</div>
</template>
<style scoped>
/* 自定义样式 */
button {
outline: none;
border: none;
cursor: pointer;
outline: none;
border: none;
cursor: pointer;
}
button:focus {
outline: none;
outline: none;
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<div class="card flex flex-col items-center justify-center text-center">
<div class="card flex flex-col items-center justify-center text-center">
<!-- 图片插画 -->
<img src="@/assets/images/empty.svg" alt="空状态" class="w-64 h-64" />
@@ -7,23 +7,23 @@
<h2 class="text-xl font-semibold text-gray-700 mb-2">
没有查询到相关结果
</h2>
<p class="text-lg font-semibold text-gray-700 mb-2 text-center">
<!-- <p class="text-lg font-semibold text-gray-700 mb-2 text-center">
由于民政部门升级维护查询功能暂不能用<br />恢复正常后我们将通过短信及时通知您
<!-- 可能由于春节期间系统维护导致未能查询到相关信息<br />请过后再试 -->
</p>
可能由于春节期间系统维护导致未能查询到相关信息<br />请过后再试
</p> -->
<p class="text-gray-500 text-sm mb-2 leading-relaxed">
订单已申请退款预计
<span class="text-blue-500 font-medium">24小时内到账</span>
</p>
<p class="text-gray-400 text-xs">
如果已到账您可以忽略本提示
</p>
<p class="text-gray-400 text-xs">如果已到账您可以忽略本提示</p>
<!-- 返回按钮 -->
<button @click="goBack"
class="mt-4 px-6 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition duration-300 ease-in-out">
<button
@click="goBack"
class="mt-4 px-6 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition duration-300 ease-in-out"
>
返回上一页
</button>
</div>
@@ -32,10 +32,9 @@
<script setup>
const route = useRoute();
// 返回上一页逻辑
function goBack() {
route.goBack()
route.goBack();
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<div class="card my-2">
<div>
<van-icon name="info-o" class="tips-icon" />
<span class="tips-title">温馨提示</span>
</div>
<div>
<van-text-ellipsis rows="2" :content="content" expand-text="展开" collapse-text="收起" />
</div>
</div>
</template>
<script setup>
import { ref, defineProps } from 'vue';
const props = defineProps({
content: {
type: String,
required: true
}
});
const isExpanded = ref(false);
</script>
<style scoped>
.tips-card {
background: #f7faff;
border-radius: 8px;
padding: 12px;
}
.tips-icon {
color: #1989fa;
margin-right: 5px;
}
.tips-title {
font-weight: bold;
font-size: 16px;
}
.tips-content {
font-size: 14px;
color: #333;
}
</style>

View File

@@ -47,7 +47,15 @@ const useApiFetch = createFetch({
}
if (data.code !== 200) {
if (data.code !== 200002 && data.code !== 200003) {
if (data.code === 100009) {
// 改进的存储管理
localStorage.removeItem('token')
// 重置状态
location.reload()
}
if (data.code !== 200002 && data.code !== 200003 && data.code !== 200004 && data.code !== 100009) {
showToast({ message: data.msg });
}
}

View File

@@ -1,27 +0,0 @@
import { useFetch, createFetch } from '@vueuse/core';
export function useHttp(url, options = {}, token) {
const fetch = createFetch(url, {
baseUrl: '/api/v1',
options: {
async beforeFetch({ url, options, cancel }) {
console.log("asdasd", options)
options.headers = {
...options.headers,
Authorization: `${token}`,
}
return {
options,
}
},
async afterFetch(ctx) {
console.log("ctx", ctx)
// if (ctx.data.code !== 200) {
// throw new Error(ctx.data.message || '请求失败');
// }
return ctx;
},
}
})
return fetch(url)
}

View File

@@ -27,6 +27,10 @@ import carCxsxIcon from "@/assets/images/car_cheliangshangxian.png";
import carCjhccIcon from "@/assets/images/car_vinchache.png";
import carWbxxIcon from "@/assets/images/car_weibao.png";
import dqfxIcon from "@/assets/images/dqfx_icon.svg";
import jzfxIcon from "@/assets/images/jzfx_icon.svg";
import rzfxIcon from "@/assets/images/rzfx_icon.svg";
import zlfxIcon from "@/assets/images/zlfx_icon.svg";
// 定义所有菜单项
const allMenuItems = [
{
@@ -38,7 +42,7 @@ const allMenuItems = [
"通过查询个人的不良记录,评估其风险等级(低、中、高风险)。帮助您识别潜在的信用和法律风险,确保合作方的合法合规。",
},
{
title: "人企关系",
title: "股东人企关系",
icon: gdrqgxIcon,
product: "toc_ShareholderBusinessRelation",
category: "个人风险查询",
@@ -134,12 +138,28 @@ const allMenuItems = [
"查询身份证号码的归属地信息,包括发证地区和行政区划,验证身份信息的准确性。",
},
{
title: "学历认证",
title: "学历核验",
icon: xlhyIcon,
product: "toc_EducationVerification",
category: "个人风险查询",
description:
"验证个人学历信息的真实性,提供教育背景核实服务,帮助您确认所提供学历证明的准确性和可靠性。",
"验证个人学历证书的真实性,提供教育背景核实服务,帮助您确认所提供学历证明的准确性和可靠性。",
},
{
title: "学历信息查询",
icon: xlhyIcon,
product: "toc_EducationInfo",
category: "个人风险查询",
description:
"查询个人学历信息,包括学历证书编号、毕业院校、专业名称等,帮助您了解个人的教育背景和学习经历。",
},
{
title: "还款压力",
icon: grblIcon,
product: "toc_DebtRepayStress",
category: "个人风险查询",
description:
"查询个人还款压力,基于大数据分析得出的个性化评分指标,直观反映用户当前的还款能力状态。系统通过分析海量用户的借贷行为、还款记录和财务状况,生成一个科学的分值,帮助您了解自身财务健康度。分值越低,表示还款压力越小;分值越高,则表示潜在还款压力较大,建议合理规划财务。定期查看您的还款压力分析,及时调整借贷策略,保持健康的财务状态。",
},
{
title: "手机三要素",
@@ -275,7 +295,14 @@ const allMenuItems = [
"车辆估值功能帮助用户评估车辆的市场价值,提供基于车型、年限、车况等因素的估值报告,帮助用户做出购买或出售决策。",
},
{
title: "双人婚姻",
title: "婚姻状态",
icon: srhyIcon,
product: "toc_Marriage",
category: "个人风险查询",
description: "查询个人婚姻状态,帮助评估婚姻的法律状态和风险。",
},
{
title: "双人婚姻核验",
icon: srhyIcon,
product: "toc_DualMarriage",
category: "个人风险查询",
@@ -295,6 +322,56 @@ const allMenuItems = [
category: "个人风险查询",
description: "查询个人限高记录,帮助评估个人信用状况。",
},
{
title: "婚恋风险",
icon: srhyIcon,
product: "marriage",
category: "风险报告",
description: "评估婚恋对象的潜在风险,提供全面的背景调查和风险分析,帮助用户做出明智的婚恋决策。",
},
{
title: "老板企业风险",
icon: gdrqgxIcon,
product: "companyinfo",
category: "风险报告",
description: "全面评估企业老板的各类风险,包括信用风险、法律风险和经营风险,为商业合作提供决策依据。",
},
{
title: "租赁风险",
icon: zlfxIcon,
product: "rentalinfo",
category: "风险报告",
description: "评估租赁关系中的潜在风险,对租户进行全面的信用调查,帮助房东降低租赁风险,保障租赁双方权益。",
},
{
title: "个人风险",
icon: grblIcon,
product: "riskassessment",
category: "风险报告",
description: "提供全面的个人风险评估,包括信用风险、法律风险和行为风险,帮助用户了解个人的整体风险状况。",
},
{
title: "入职风险",
icon: rzfxIcon,
product: "backgroundcheck",
category: "风险报告",
description: "为企业提供求职者的背景调查服务,包括信用风险、法律风险和行为风险,降低企业用人风险。",
},
{
title: "贷前风险",
icon: dqfxIcon,
product: "preloanbackgroundcheck",
category: "风险报告",
description: "在贷款发放前评估借款人的风险状况,包括信用历史、还款能力和潜在违约风险,为贷款决策提供支持。",
},
{
title: "家政风险",
icon: jzfxIcon,
product: "homeservice",
category: "风险报告",
description: "评估家政服务人员的背景和风险,确保家庭安全,为家庭雇主提供可靠的家政人员风险评估。",
},
];
// 定义 Composable返回过滤后的菜单项

View File

@@ -2,7 +2,11 @@
<div class="home-layout min-h-screen flex flex-col">
<!-- Header -->
<div class="header">
<img class="logo rounded-full" src="@/assets/images/logo.jpg" alt="Logo" />
<img
class="logo rounded-full"
src="@/assets/images/logo.jpg"
alt="Logo"
/>
<div class="title">天远查</div>
</div>
@@ -14,8 +18,13 @@
<!-- Vant Tabbar -->
<van-tabbar v-model="tabbar" @change="tabChange">
<van-tabbar-item v-for="(item, index) in menu" :key="index" :name="item.name" :icon="item.icon">{{
item.title }} </van-tabbar-item>
<van-tabbar-item
v-for="(item, index) in menu"
:key="index"
:name="item.name"
:icon="item.icon"
>{{ item.title }}
</van-tabbar-item>
</van-tabbar>
<!-- Complaint Button -->
@@ -31,27 +40,23 @@
</a>
</div>
</div>
<div>
广西福铭网络科技有限公司版权所有
</div>
<div>广西福铭网络科技有限公司版权所有</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router'; // 引入 Vue Router
import { ref, reactive, onMounted } from "vue";
import { useRoute, useRouter } from "vue-router"; // 引入 Vue Router
const router = useRouter();
const route = useRoute();
const tabbar = ref('index');
const tabbar = ref("index");
const menu = reactive([
{ title: '首页', icon: 'home-o', name: 'index' },
{ title: 'AI律师', icon: 'chat-o', name: 'ai' },
{ title: '我的', icon: 'user-o', name: 'me' },
{ title: "首页", icon: "home-o", name: "index" },
{ title: "AI律师", icon: "chat-o", name: "ai" },
{ title: "我的", icon: "user-o", name: "me" },
{ title: "推广代理", icon: "balance-o", name: "promote" },
]);
// 根据当前路由设置 Tabbar 的高亮项
@@ -60,19 +65,23 @@ onMounted(() => {
tabbar.value = currentPage;
});
const onClickOverlay = () => { }
const onClickOverlay = () => {};
// 跳转到相应页面
const tabChange = (name) => {
router.push({ name }); // 使用 Vue Router 进行跳转
if (name === "promote") {
window.location.href =
"https://www.tianyuandb.com?_um_campaign=67c15d4f8f232a05f1221b82&_um_channel=67c15d4f8f232a05f1221b83"; // 跳转到客服页面
} else {
router.push({ name }); // 使用 Vue Router 进行跳转
}
};
// 跳转到投诉页面
const toComplaint = () => {
window.location.href = 'https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9' // 跳转到客服页面
window.location.href =
"https://work.weixin.qq.com/kfid/kfc5c19b2b93a5e73b9"; // 跳转到客服页面
// router.push({ name: 'complaint' }); // 使用 Vue Router 进行跳转
};
</script>
<style scoped>

View File

@@ -1,35 +1,42 @@
<template>
<van-nav-bar fixed :border="false" placeholder :title="pageTitle" left-text="返回" left-arrow
@click-left="onClickLeft" />
<van-nav-bar
fixed
:border="false"
placeholder
:title="pageTitle"
left-text="返回"
left-arrow
@click-left="onClickLeft"
z-index="1000"
/>
<router-view />
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ref, watch } from "vue";
import { useRouter, useRoute } from "vue-router";
const router = useRouter()
const route = useRoute()
const pageTitle = ref('') // 用来保存页面标题
const router = useRouter();
const route = useRoute();
const pageTitle = ref(""); // 用来保存页面标题
const onClickLeft = () => {
if (route.name === 'report') {
router.replace('/historyQuery')
} else if (route.name === 'history') {
router.replace('/')
if (route.name === "report") {
router.replace("/historyQuery");
} else if (route.name === "history") {
router.replace("/");
} else {
router.back()
router.back();
}
}
onMounted(() => {
})
};
onMounted(() => {});
// 监听路由变化并更新标题
watch(
() => route.meta.title,
(newTitle) => {
pageTitle.value = newTitle || '默认标题'
pageTitle.value = newTitle || "默认标题";
},
{ immediate: true }
)
);
</script>
<style lang="scss" scoped></style>

View File

@@ -71,7 +71,14 @@ const router = createRouter({
name: "authorization",
component: () =>
import("@/views/Authorization.vue"),
meta: { title: "授权书" },
meta: { title: "授权书同意书" },
},
{
path: "/othersAuthorization",
name: "othersAuthorization",
component: () =>
import("@/views/OthersAuthorization.vue"),
meta: { title: "他人授权同意书" },
},
{
path: "/privacyPolicy",

View File

@@ -1,265 +1,362 @@
<script setup>
import { defineProps, onMounted, ref, watch } from "vue";
import * as echarts from "echarts"; // 引入 ECharts
import LTitle from "@/components/LTitle.vue";
import { defineProps, onMounted, ref, watch } from 'vue'
import * as echarts from 'echarts' // 引入 ECharts
import LTitle from '@/components/LTitle.vue'
const props = defineProps({
data: {
type: Object,
required: true,
},
});
data: {
type: Object,
required: true,
},
})
const chartInstance = ref(null); // ECharts 实例
const chartRef = ref(null); // 图表容器 DOM
const riskLevel = ref("");
const riskLevelText = ref("");
const riskLevelClass = ref("");
const chartInstance = ref(null) // ECharts 实例
const chartRef = ref(null) // 图表容器 DOM
const riskLevel = ref('')
const riskLevelText = ref('')
const riskLevelClass = ref('')
const riskDescription = ref('')
const riskInfoList = ref([
{
level: "无风险",
description: "无任何不良风险记录",
class: "text-green-600",
},
{ level: "低风险", description: "涉稳、寻衅滋事", class: "text-yellow-500" },
{
level: "中风险",
description: "吸毒、涉毒、犯罪前科",
class: "text-orange-500",
},
{
level: "高风险",
description: "涉案人员、在逃、犯罪嫌疑人",
class: "text-red-500",
},
]);
{
level: '无风险',
description: '无任何不良风险记录',
class: 'text-green-600',
},
{ level: '低风险', description: '涉稳、寻衅滋事', class: 'text-yellow-500' },
{
level: '中风险',
description: '吸毒、涉毒、犯罪前科',
class: 'text-orange-500',
},
{
level: '高风险',
description: '涉案人员、在逃、犯罪嫌疑人',
class: 'text-red-500',
},
])
// 根据风险等级动态设置图表配置
function getChartOption(level) {
const valueMap = {
A: 10, // 无风险
F: 30, // 低风险
C: 60, // 中风险
D: 60, // 中风险
B: 90, // 高风险
E: 90, // 高风险
default: 0, // 未知风险
};
const labelNameMap = {
A: "无风险", // 无风险
F: "低风险", // 低风险
C: "中风险", // 中风险
D: "中风险", // 中风险
B: "高风险", // 高风险
E: "高风险", // 高风险
default: "未知风险", // 未知风险
};
const value = valueMap[level] || valueMap.default;
const labelName = labelNameMap[level] || valueMap.default;
return {
tooltip: {
formatter: "{a} <br/>{b} : {c}",
const valueMap = {
A: 10, // 无风险
F: 30, // 低风险
C: 60, // 中风险
D: 60, // 中风险
B: 90, // 高风险
E: 90, // 高风险
default: 0, // 未知风险
}
const labelNameMap = {
A: '无风险', // 无风险
F: '低风险', // 低风险
C: '中风险', // 中风险
D: '中风险', // 中风险
B: '高风险', // 高风险
E: '高风险', // 高风险
default: '未知风险', // 未知风险
}
const value = valueMap[level] || valueMap.default
const labelName = labelNameMap[level] || labelNameMap.default
// 根据不同风险等级设置不同的颜色方案
let colorGradient = []
let detailColor = '#333'
switch (level) {
case 'A': // 无风险
colorGradient = [
{ offset: 0, color: '#52c41a' },
{ offset: 1, color: '#7fdb42' },
]
detailColor = '#52c41a'
break
case 'F': // 低风险
colorGradient = [
{ offset: 0, color: '#faad14' },
{ offset: 1, color: '#ffc53d' },
]
detailColor = '#faad14'
break
case 'C': // 中风险
case 'D':
colorGradient = [
{ offset: 0, color: '#fa8c16' },
{ offset: 1, color: '#ffa940' },
]
detailColor = '#fa8c16'
break
case 'B': // 高风险
case 'E':
colorGradient = [
{ offset: 0, color: '#f5222d' },
{ offset: 1, color: '#ff4d4f' },
]
detailColor = '#f5222d'
break
default:
colorGradient = [
{ offset: 0, color: '#999' },
{ offset: 1, color: '#ccc' },
]
detailColor = '#999'
}
return {
series: [
{
type: 'gauge',
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
radius: '100%',
center: ['50%', '70%'],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, colorGradient),
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.2)',
},
series: [
{
name: "风险分值",
type: "gauge",
min: 0,
max: 100,
splitNumber: 5, // 分成 5 个区间
radius: "90%",
detail: {
formatter: "{value}", // 显示分值
fontSize: 18,
fontWeight: "bold",
color: "#333",
},
data: [{ value, name: labelName }],
axisLine: {
lineStyle: {
width: 15,
shadowBlur: 15, // 添加阴影
shadowColor: "rgba(0, 0, 0, 0.3)", // 阴影颜色
color: [
[
0.25,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#10B981" },
{ offset: 1, color: "#34D399" },
]),
], // 无风险渐变
[
0.5,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#FFC300" },
{ offset: 1, color: "#EAB308" },
]),
], // 低风险渐变
[
0.75,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#F97316" },
{ offset: 1, color: "#E68416" },
]),
], // 中风险渐变
[
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#EF4444" },
{ offset: 1, color: "#FF6347" },
]),
], // 高风险渐变
],
},
},
pointer: {
icon: "path://M512 0L560 200H464L512 0Z", // 自定义指针形状
length: "70%", // 指针长度
width: 8,
itemStyle: {
color: "#FF6347", // 指针颜色
borderWidth: 2,
borderColor: "#FF4500",
shadowBlur: 8, // 添加阴影
shadowColor: "rgba(255, 99, 71, 0.5)", // 阴影颜色
},
},
title: {
offsetCenter: [0, "75%"], // 标题位置
fontSize: 20,
fontWeight: "bold",
},
detail: {
formatter: "{value}", // 显示分值
fontSize: 18,
fontWeight: "bold",
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#FF7F50" },
{ offset: 1, color: "#FF4500" },
]), // 渐变文本颜色
},
anchor: {
show: true, // 显示圆心锚点
size: 14,
itemStyle: {
color: "#FF6347", // 圆心颜色
borderWidth: 2,
borderColor: "#FF4500",
shadowBlur: 8, // 添加阴影
shadowColor: "rgba(255, 69, 0, 0.5)", // 阴影颜色
},
},
splitLine: {
length: 20, // 刻度线长度
lineStyle: {
width: 2,
color: "#999",
shadowBlur: 5, // 刻度阴影
shadowColor: "rgba(0, 0, 0, 0.2)",
},
},
axisTick: {
length: 10, // 刻度线长度
lineStyle: {
width: 1,
color: "#999",
shadowBlur: 5, // 刻度阴影
shadowColor: "rgba(0, 0, 0, 0.2)",
},
},
axisLabel: {
color: "#666", // 标签颜色
fontSize: 12,
shadowBlur: 5,
shadowColor: "rgba(0, 0, 0, 0.2)", // 标签阴影
},
progress: {
show: true,
width: 20,
roundCap: true,
clip: false,
},
axisLine: {
roundCap: true,
lineStyle: {
width: 20,
color: [
[
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: 'rgba(0, 0, 0, 0.1)',
},
{
offset: 1,
color: 'rgba(0, 0, 0, 0.05)',
},
]),
],
],
},
},
axisTick: {
show: false,
},
splitLine: {
show: true,
distance: -26,
length: 5,
lineStyle: {
color: '#999',
width: 2,
},
},
axisLabel: {
show: true,
distance: -8,
fontSize: 12,
color: '#999',
formatter: function (value) {
if (value % 20 === 0) {
return value
}
return ''
},
},
anchor: {
show: false,
},
pointer: {
icon: 'triangle',
iconStyle: {
color: detailColor,
borderColor: detailColor,
borderWidth: 1,
},
offsetCenter: ['7%', '-67%'],
length: '10%',
width: 15,
},
detail: {
valueAnimation: true,
fontSize: 30,
fontWeight: 'bold',
color: detailColor,
offsetCenter: [0, '-25%'],
formatter: function (value) {
return `{value|${value}分}\n{level|${labelName}}`
},
rich: {
value: {
fontSize: 30,
fontWeight: 'bold',
color: detailColor,
padding: [0, 0, 5, 0],
},
level: {
fontSize: 14,
fontWeight: 'normal',
color: detailColor,
padding: [5, 0, 0, 0],
},
},
},
data: [
{
value,
},
],
};
},
],
}
}
// 获取风险描述
function getRiskDescription(level) {
switch (level) {
case 'A':
return '根据综合分析,当前报告未检测到任何不良风险记录。各项指标表现正常,总体状况良好,可以放心处理。'
case 'F':
return '当前报告显示存在低风险情况,主要涉及涉稳或寻衅滋事类问题。建议关注相关指标变化,保持适当警惕。'
case 'C':
case 'D':
return '注意!当前报告显示存在中等风险,涉及吸毒、涉毒或有犯罪前科等问题。建议进一步核实相关情况,谨慎处理。'
case 'B':
case 'E':
return '警告!当前报告显示高度风险状态,检测到涉案人员、在逃人员或犯罪嫌疑人特征。请立即采取相应安全措施,并向相关部门报告。'
default:
return '无法获取风险评估信息,请检查数据来源。'
}
}
// 根据风险等级动态设置文本和样式
function setRiskProperties(level) {
switch (level) {
case "A":
riskLevelText.value = "无风险";
riskLevelClass.value = "text-green-600";
break;
case "F":
riskLevelText.value = "低风险";
riskLevelClass.value = "text-lime-600";
break;
case "C":
case "D":
riskLevelText.value = "中风险";
riskLevelClass.value = "text-orange-400";
break;
case "B":
case "E":
riskLevelText.value = "高风险";
riskLevelClass.value = "text-red-500";
break;
default:
riskLevelText.value = "未知风险";
riskLevelClass.value = "text-gray-500";
}
switch (level) {
case 'A':
riskLevelText.value = '无风险'
riskLevelClass.value = 'text-green-600'
break
case 'F':
riskLevelText.value = '低风险'
riskLevelClass.value = 'text-yellow-500'
break
case 'C':
case 'D':
riskLevelText.value = '中风险'
riskLevelClass.value = 'text-orange-500'
break
case 'B':
case 'E':
riskLevelText.value = '高风险'
riskLevelClass.value = 'text-red-500'
break
default:
riskLevelText.value = '未知风险'
riskLevelClass.value = 'text-gray-500'
}
riskDescription.value = getRiskDescription(level)
}
// 初始化图表
function initChart() {
if (!chartRef.value) return;
if (!chartRef.value) return
if (!chartInstance.value) {
chartInstance.value = echarts.init(chartRef.value);
if (!chartInstance.value) {
chartInstance.value = echarts.init(chartRef.value)
}
const option = getChartOption(riskLevel.value)
chartInstance.value.setOption(option)
// 处理窗口大小变化
window.addEventListener('resize', () => {
if (chartInstance.value) {
chartInstance.value.resize()
}
const option = getChartOption(riskLevel.value);
chartInstance.value.setOption(option);
})
}
// 更新图表
function updateChart(level) {
const option = getChartOption(level);
chartInstance.value.setOption(option, true);
if (!chartInstance.value) return
const option = getChartOption(level)
chartInstance.value.setOption(option, true)
}
onMounted(() => {
riskLevel.value = props.data.risk_level;
setRiskProperties(riskLevel.value);
initChart();
});
riskLevel.value = props.data.risk_level
setRiskProperties(riskLevel.value)
initChart()
})
watch(riskLevel, (newLevel) => {
setRiskProperties(newLevel);
updateChart(newLevel);
});
watch(
() => props.data.risk_level,
newLevel => {
riskLevel.value = newLevel
setRiskProperties(newLevel)
updateChart(newLevel)
}
)
// 在组件销毁前清理
onUnmounted(() => {
if (chartInstance.value) {
chartInstance.value.dispose()
chartInstance.value = null
}
window.removeEventListener('resize', () => chartInstance.value?.resize())
})
</script>
<template>
<div class="card">
<div class="span-2xl font-bold" :class="riskLevelClass">
<div ref="chartRef" class="chart-container"></div>
</div>
<div class="mt-6">
<LTitle title="信息解读" type="blue-green" />
<div class="mt-4 rounded-lg bg-slate-100/80 px-4 py-2 space-y-4">
<div v-for="(item, index) in riskInfoList" :key="index" class="flex items-center justify-between">
<span class="font-bold" :class="[item.class]">
{{ item.level }}
</span>
<span class="span-gray-700">
{{ item.description }}
</span>
</div>
</div>
</div>
<div class="card">
<div class="span-2xl font-bold" :class="riskLevelClass">
<div ref="chartRef" class="chart-container"></div>
</div>
<!-- 风险描述区域 -->
<div class="risk-description" :class="riskLevelClass">
{{ riskDescription }}
</div>
<div class="mt-6">
<LTitle title="信息解读" type="blue-green" />
<div class="mt-4 rounded-lg bg-slate-100/80 px-4 py-2 space-y-4">
<div v-for="(item, index) in riskInfoList" :key="index" class="flex items-center justify-between">
<span class="font-bold" :class="[item.class]">
{{ item.level }}
</span>
<span class="span-gray-700">
{{ item.description }}
</span>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.chart-container {
width: 100%;
height: 300px;
width: 100%;
height: 210px;
}
.risk-description {
margin-top: 10px;
padding: 10px 15px;
background-color: #f5f5f5;
border-radius: 6px;
color: #333;
font-size: 13px;
line-height: 1.5;
text-align: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,746 @@
<script setup>
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
})
// 风险等级转换为文字描述
const riskLevelText = (level, type) => {
if (type === 'black_gray_level') {
const levels = {
'': '无风险',
1: '低风险',
2: '中等风险',
3: '高风险',
4: '极高风险',
}
return levels[level] || '未知风险'
} else if (type === 'telefraud_level') {
const levels = {
0: '无风险',
1: '极低风险',
2: '低风险',
3: '中低风险',
4: '中等风险',
5: '高风险',
6: '极高风险',
}
return levels[level] || '未知风险'
} else if (type === 'frg_list_level') {
if (level >= '3' && level <= '5') return '低风险团伙'
if (level >= '6' && level <= '7') return '中风险团伙'
if (level >= '8' && level <= '10') return '高风险团伙'
return '无风险'
} else if (type === 'risk_level') {
const levels = {
A: '无风险',
F: '低风险',
C: '中风险',
D: '中风险',
B: '高风险',
E: '高风险',
}
return levels[level] || '未知风险'
} else if (type === 'gaming') {
const levelNum = parseInt(level)
if (levelNum === 0) return '无风险'
if (levelNum > 0 && levelNum <= 20) return '极低风险'
if (levelNum > 20 && levelNum <= 40) return '低风险'
if (levelNum > 40 && levelNum <= 60) return '中等风险'
if (levelNum > 60 && levelNum <= 80) return '高风险'
if (levelNum > 80) return '极高风险'
return '未知风险'
}
return '未知风险'
}
// 风险等级转换为颜色
const riskLevelColor = (level, type) => {
if (type === 'black_gray_level') {
if (level === '' || level === '1') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
if (level === '2') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
if (level === '3') return 'bg-gradient-to-r from-orange-400 to-amber-600'
if (level === '4') return 'bg-gradient-to-r from-rose-400 to-red-500'
return 'bg-gradient-to-r from-gray-400 to-gray-500'
} else if (type === 'telefraud_level') {
if (level === '0') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
if (level === '1' || level === '2') return 'bg-gradient-to-r from-teal-300 to-green-400'
if (level === '3' || level === '4') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
if (level === '5') return 'bg-gradient-to-r from-orange-400 to-amber-600'
if (level === '6') return 'bg-gradient-to-r from-rose-400 to-red-500'
return 'bg-gradient-to-r from-gray-400 to-gray-500'
} else if (type === 'frg_list_level') {
if (level >= '3' && level <= '5') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
if (level >= '6' && level <= '7') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
if (level >= '8' && level <= '10') return 'bg-gradient-to-r from-rose-400 to-red-500'
return 'bg-gradient-to-r from-gray-400 to-gray-500'
} else if (type === 'risk_level') {
if (level === 'A') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
if (level === 'F') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
if (level === 'C' || level === 'D') return 'bg-gradient-to-r from-orange-400 to-amber-600'
if (level === 'B' || level === 'E') return 'bg-gradient-to-r from-rose-400 to-red-500'
return 'bg-gradient-to-r from-gray-400 to-gray-500'
} else if (type === 'gaming') {
const levelNum = parseInt(level)
if (levelNum === 0) return 'bg-gradient-to-r from-emerald-400 to-teal-500'
if (levelNum > 0 && levelNum <= 20) return 'bg-gradient-to-r from-teal-300 to-green-400'
if (levelNum > 20 && levelNum <= 40) return 'bg-gradient-to-r from-green-400 to-green-500'
if (levelNum > 40 && levelNum <= 60) return 'bg-gradient-to-r from-amber-400 to-yellow-500'
if (levelNum > 60 && levelNum <= 80) return 'bg-gradient-to-r from-orange-400 to-amber-600'
if (levelNum > 80) return 'bg-gradient-to-r from-rose-400 to-red-500'
return 'bg-gradient-to-r from-gray-400 to-gray-500'
}
return 'bg-gradient-to-r from-gray-400 to-gray-500'
}
// 根据风险类型获取名称
const getRiskTypeName = type => {
const types = {
110: '疑似欺诈',
130: '疑似赌博庄家',
150: '疑似赌博玩家',
170: '疑似涉赌跑分',
}
return types[type] || '未知类型'
}
// 获取团伙规模描述
const getGroupSizeDesc = code => {
const sizes = {
a: '小规模(少于50人)',
b: '中等规模(50-100人)',
c: '大规模(100-500人)',
d: '超大规模(500人以上)',
}
return sizes[code] || '未知规模'
}
// 获取风险图标
const getRiskIcon = type => {
switch (type) {
case '110':
return 'fa-exclamation-triangle'
case '130':
return 'fa-dice'
case '150':
return 'fa-gamepad'
case '170':
return 'fa-money-bill-wave'
default:
return 'fa-question-circle'
}
}
// 获取不良记录详情
const getRiskLevelDetail = level => {
switch (level) {
case 'A':
return '无任何不良记录'
case 'F':
return '涉稳、寻衅滋事'
case 'C':
case 'D':
return '吸毒、涉毒、犯罪前科'
case 'B':
case 'E':
return '涉案人员、在逃、犯罪嫌疑人'
default:
return '未知记录'
}
}
// 风险评估总结
const getRiskSummary = () => {
if (!props.data) return { text: '无法评估风险', level: 'low', color: 'text-gray-500' }
let highRiskCount = 0
let mediumRiskCount = 0
// 检查黑灰产等级
if (props.data.black_gray_level && parseInt(props.data.black_gray_level) > 2) {
highRiskCount++
} else if (props.data.black_gray_level && parseInt(props.data.black_gray_level) === 2) {
mediumRiskCount++
}
// 检查电诈风险
if (props.data.telefraud_level && parseInt(props.data.telefraud_level) > 4) {
highRiskCount++
} else if (props.data.telefraud_level && parseInt(props.data.telefraud_level) > 2) {
mediumRiskCount++
}
// 检查团伙欺诈
if (
props.data.fraud_group &&
props.data.fraud_group.frg_list_level &&
parseInt(props.data.fraud_group.frg_list_level) > 7
) {
highRiskCount++
} else if (
props.data.fraud_group &&
props.data.fraud_group.frg_list_level &&
parseInt(props.data.fraud_group.frg_list_level) > 5
) {
mediumRiskCount++
}
// 检查风险等级
if (props.data.risk_level && props.data.risk_level.risk_level) {
if (['B', 'E'].includes(props.data.risk_level.risk_level)) {
highRiskCount++
} else if (['C', 'D'].includes(props.data.risk_level.risk_level)) {
mediumRiskCount++
} else if (props.data.risk_level.risk_level === 'F') {
// 低风险,不增加计数
}
}
// 检查反诈反赌核验
if (props.data.anti_fraud_gaming) {
props.data.anti_fraud_gaming.forEach(item => {
const levelNum = parseInt(item.riskLevel)
if (levelNum > 60) {
highRiskCount++
} else if (levelNum > 40) {
mediumRiskCount++
}
})
}
if (highRiskCount > 0) {
return {
text: '该用户存在较高风险行为,建议进行进一步核实和监控',
level: 'high',
color: 'text-red-500',
}
} else if (mediumRiskCount > 0) {
return {
text: '该用户存在一定风险行为,建议提高警惕',
level: 'medium',
color: 'text-yellow-500',
}
} else {
return {
text: '该用户行为正常,风险较低',
level: 'low',
color: 'text-green-500',
}
}
}
const summary = getRiskSummary()
</script>
<template>
<div class="card main-card">
<div v-if="!data || Object.keys(data).length === 0" class="py-4 text-center text-gray-500">
暂无风险行为扫描数据
</div>
<div v-else class="risk-content">
<!-- 风险总结 -->
<div
class="summary-card"
:class="{
'border-red-500 glow-red': summary.level === 'high',
'border-yellow-500 glow-yellow': summary.level === 'medium',
'border-green-500 glow-green': summary.level === 'low',
}"
>
<div class="flex items-center">
<div class="summary-icon" :class="summary.color">
<i
class="fas"
:class="
summary.level === 'high'
? 'fa-exclamation-triangle'
: summary.level === 'medium'
? 'fa-exclamation-circle'
: 'fa-check-circle'
"
></i>
</div>
<div class="font-bold text-lg" :class="summary.color">风险评估总结</div>
</div>
<div class="mt-1 text-gray-700">{{ summary.text }}</div>
</div>
<div class="grid-container">
<!-- 左侧列 -->
<div class="grid-left">
<!-- 黑灰产等级 -->
<div class="risk-section hover-lift">
<div class="section-title flex items-center">
<div class="title-icon bg-indigo-100 text-indigo-600">
<i class="fas fa-user-secret"></i>
</div>
<span>黑灰产等级</span>
</div>
<div class="section-content">
<div class="risk-level-indicator">
<div class="indicator-label">风险等级</div>
<div class="indicator-bar">
<div
class="indicator-value"
:class="riskLevelColor(data.black_gray_level || '', 'black_gray_level')"
:style="{
width: data.black_gray_level ? `${Math.min(parseInt(data.black_gray_level) * 25, 100)}%` : '0%',
}"
></div>
</div>
<div
class="indicator-text"
:class="{
'text-green-500': (data.black_gray_level || '') === '' || (data.black_gray_level || '') === '1',
'text-yellow-500': (data.black_gray_level || '') === '2',
'text-orange-500': (data.black_gray_level || '') === '3',
'text-red-500': (data.black_gray_level || '') === '4',
}"
>
{{ riskLevelText(data.black_gray_level || '', 'black_gray_level') }}
</div>
</div>
<div class="description">黑灰产等级评估用户是否参与非法活动等级越高风险越大</div>
</div>
</div>
<!-- 电诈风险预警 -->
<div class="risk-section hover-lift">
<div class="section-title flex items-center">
<div class="title-icon bg-red-100 text-red-600">
<i class="fas fa-phone-slash"></i>
</div>
<span>电诈风险预警</span>
</div>
<div class="section-content">
<div class="risk-level-indicator">
<div class="indicator-label">风险等级</div>
<div class="indicator-bar">
<div
class="indicator-value"
:class="riskLevelColor(data.telefraud_level || '0', 'telefraud_level')"
:style="{ width: `${Math.min(parseInt(data.telefraud_level || '0') * 16.6, 100)}%` }"
></div>
</div>
<div
class="indicator-text"
:class="{
'text-green-500':
(data.telefraud_level || '0') === '0' ||
(data.telefraud_level || '0') === '1' ||
(data.telefraud_level || '0') === '2',
'text-yellow-500': (data.telefraud_level || '0') === '3' || (data.telefraud_level || '0') === '4',
'text-orange-500': (data.telefraud_level || '0') === '5',
'text-red-500': (data.telefraud_level || '0') === '6',
}"
>
{{ riskLevelText(data.telefraud_level || '0', 'telefraud_level') }}
</div>
</div>
<div class="description">电诈风险预警评估用户是否涉及电信诈骗活动值越大风险越高</div>
</div>
</div>
<!-- 综合风险等级 -->
<div class="risk-section hover-lift">
<div class="section-title flex items-center">
<div class="title-icon bg-emerald-100 text-emerald-600">
<i class="fas fa-shield-alt"></i>
</div>
<span>不良个人核查</span>
</div>
<div class="section-content">
<div v-if="data.risk_level" class="flex items-center justify-center py-3">
<div
class="risk-level-badge"
:class="{
'bg-green-100 text-green-700 badge-pulse-green': data.risk_level.risk_level === 'A',
'bg-yellow-100 text-yellow-700 badge-pulse-yellow': data.risk_level.risk_level === 'F',
'bg-orange-100 text-orange-700 badge-pulse-orange': ['C', 'D'].includes(data.risk_level.risk_level),
'bg-red-100 text-red-700 badge-pulse-red': ['B', 'E'].includes(data.risk_level.risk_level),
}"
>
<span class="text-xl font-bold">{{
riskLevelText(data.risk_level.risk_level || 'A', 'risk_level')
}}</span>
</div>
<div class="ml-4 text-sm">
<div class="font-medium">详情:</div>
<div
class="mt-1"
:class="{
'text-green-600': data.risk_level.risk_level === 'A',
'text-yellow-600': data.risk_level.risk_level === 'F',
'text-orange-600': ['C', 'D'].includes(data.risk_level.risk_level),
'text-red-600': ['B', 'E'].includes(data.risk_level.risk_level),
}"
>
{{ getRiskLevelDetail(data.risk_level.risk_level || 'A') }}
</div>
</div>
</div>
<div v-else class="text-center py-2 text-gray-500">暂无不良个人核查数据</div>
<div class="description">不良个人核查评估用户的风险状况从无风险到高风险分级</div>
</div>
</div>
</div>
<!-- 右侧列 -->
<div class="grid-right">
<!-- 团伙欺诈排查 -->
<div class="risk-section hover-lift">
<div class="section-title flex items-center">
<div class="title-icon bg-amber-100 text-amber-600">
<i class="fas fa-users-slash"></i>
</div>
<span>团伙欺诈排查</span>
</div>
<div class="section-content">
<div v-if="data.fraud_group" class="flex flex-col md:flex-row gap-3">
<div class="risk-level-indicator flex-1">
<div class="indicator-label">团伙风险等级</div>
<div class="indicator-bar">
<div
class="indicator-value"
:class="riskLevelColor(data.fraud_group.frg_list_level || '3', 'frg_list_level')"
:style="{
width: `${Math.min((parseInt(data.fraud_group.frg_list_level || '3') - 2) * 12.5, 100)}%`,
}"
></div>
</div>
<div
class="indicator-text"
:class="{
'text-green-500': parseInt(data.fraud_group.frg_list_level || '3') <= 5,
'text-yellow-500':
parseInt(data.fraud_group.frg_list_level || '3') >= 6 &&
parseInt(data.fraud_group.frg_list_level || '3') <= 7,
'text-red-500': parseInt(data.fraud_group.frg_list_level || '3') >= 8,
}"
>
{{ riskLevelText(data.fraud_group.frg_list_level || '3', 'frg_list_level') }}
</div>
</div>
<div class="group-size flex-1">
<div class="font-medium text-gray-700">团伙规模</div>
<div class="mt-2 flex items-center">
<i class="fas fa-users text-blue-500 mr-2 text-xl"></i>
<span>{{ getGroupSizeDesc(data.fraud_group.frg_group_num || 'a') }}</span>
</div>
</div>
</div>
<div v-else class="text-center py-2 text-gray-500">暂无团伙欺诈数据</div>
<div class="description mt-1">团伙欺诈排查评估用户是否属于欺诈团伙及团伙规模大小</div>
</div>
</div>
<!-- 反诈反赌核验 -->
<div class="risk-section hover-lift">
<div class="section-title flex items-center">
<div class="title-icon bg-purple-100 text-purple-600">
<i class="fas fa-dice-slash"></i>
</div>
<span>反诈反赌核验</span>
</div>
<div class="section-content">
<div v-if="data.anti_fraud_gaming && data.anti_fraud_gaming.length > 0" class="grid grid-cols-1 gap-3">
<div
v-for="(item, index) in data.anti_fraud_gaming"
:key="index"
class="gaming-item"
:class="
parseInt(item.riskLevel) === 0
? 'border-green-500'
: parseInt(item.riskLevel) < 4
? 'border-green-400'
: parseInt(item.riskLevel) < 7
? 'border-yellow-500'
: 'border-red-500'
"
>
<div
class="gaming-icon"
:class="
parseInt(item.riskLevel) === 0
? 'bg-green-100 text-green-500'
: parseInt(item.riskLevel) < 4
? 'bg-green-100 text-green-500'
: parseInt(item.riskLevel) < 7
? 'bg-yellow-100 text-yellow-600'
: 'bg-red-100 text-red-500'
"
>
<i class="fas" :class="getRiskIcon(item.riskType)"></i>
</div>
<div class="flex-1">
<div class="font-medium text-sm">{{ getRiskTypeName(item.riskType) }}</div>
<div class="flex items-center mt-2">
<div class="progress-container">
<div
class="progress-bar"
:class="riskLevelColor(item.riskLevel, 'gaming')"
:style="{ width: `${Math.min(parseInt(item.riskLevel), 100)}%` }"
></div>
</div>
<span
class="risk-level-text"
:class="{
'text-green-500': parseInt(item.riskLevel) <= 20,
'text-green-600': parseInt(item.riskLevel) > 20 && parseInt(item.riskLevel) <= 40,
'text-yellow-500': parseInt(item.riskLevel) > 40 && parseInt(item.riskLevel) <= 60,
'text-orange-500': parseInt(item.riskLevel) > 60 && parseInt(item.riskLevel) <= 80,
'text-red-500': parseInt(item.riskLevel) > 80,
}"
>
{{ riskLevelText(item.riskLevel, 'gaming') }}
</span>
</div>
</div>
</div>
</div>
<div v-else class="text-center py-2 text-gray-500">暂无反诈反赌核验数据</div>
<div class="description mt-1">反诈反赌核验评估用户是否有涉及诈骗或赌博活动的风险</div>
</div>
</div>
<!-- 安全建议 -->
<div class="security-tips hover-lift">
<div class="flex items-center">
<div class="title-icon bg-blue-100 text-blue-600 mr-2">
<i class="fas fa-lightbulb"></i>
</div>
<div class="font-bold text-blue-700">安全建议</div>
</div>
<div class="tip-list">
<div class="tip-item">
<i class="fas fa-check-circle text-green-500 mr-1"></i>
<span>定期更新密码使用复杂且不易猜测的密码</span>
</div>
<div class="tip-item">
<i class="fas fa-check-circle text-green-500 mr-1"></i>
<span>开启双因素认证提高账户安全性</span>
</div>
<div class="tip-item">
<i class="fas fa-check-circle text-green-500 mr-1"></i>
<span>不点击来源不明的链接或下载不明文件</span>
</div>
<div class="tip-item">
<i class="fas fa-check-circle text-green-500 mr-1"></i>
<span>不向陌生人透露个人敏感信息</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.main-card {
@apply bg-white shadow-md rounded-xl p-4 mb-3 border border-gray-100;
}
.risk-content {
@apply space-y-4;
}
.grid-container {
@apply grid grid-cols-1 md:grid-cols-2 gap-4;
}
.grid-left,
.grid-right {
@apply flex flex-col gap-4;
}
.summary-card {
@apply p-4 rounded-xl shadow-sm bg-gradient-to-br from-sky-50 to-indigo-100 border-l-4 transition-all duration-300;
}
.glow-red {
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.15);
border-color: rgba(239, 68, 68, 0.6);
}
.glow-yellow {
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.15);
border-color: rgba(245, 158, 11, 0.6);
}
.glow-green {
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
border-color: rgba(16, 185, 129, 0.6);
}
.summary-icon {
@apply mr-2 text-xl;
}
.risk-section {
@apply bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden transition-all duration-300;
}
.hover-lift:hover {
transform: translateY(-3px);
box-shadow:
0 8px 16px -2px rgba(0, 0, 0, 0.1),
0 4px 8px -2px rgba(0, 0, 0, 0.05);
}
.section-title {
@apply bg-gradient-to-r from-gray-50 to-gray-100 px-4 py-3 font-bold text-gray-700 border-b border-gray-200 flex items-center;
}
.title-icon {
@apply w-7 h-7 rounded-full flex items-center justify-center mr-3 shadow-sm;
}
.section-content {
@apply p-4;
}
.risk-level-indicator {
@apply mb-2;
}
.indicator-label {
@apply text-gray-700 font-medium mb-1 text-sm;
}
.indicator-bar {
@apply w-full bg-gray-200 rounded-full h-3 overflow-hidden shadow-inner;
}
.indicator-value {
@apply h-3 rounded-full transition-all duration-500;
}
.indicator-text {
@apply mt-1 font-medium text-sm;
}
.description {
@apply text-xs text-gray-500 mt-2 italic;
}
.risk-level-badge {
@apply flex flex-col items-center justify-center w-20 h-20 rounded-full shadow-md border transition-transform duration-300 backdrop-blur-sm;
}
.badge-pulse-green {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.3));
border-color: rgba(5, 150, 105, 0.4);
animation: pulse-green 3s infinite;
}
.badge-pulse-yellow {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(217, 119, 6, 0.3));
border-color: rgba(217, 119, 6, 0.4);
animation: pulse-yellow 3s infinite;
}
.badge-pulse-orange {
background: linear-gradient(135deg, rgba(249, 115, 22, 0.15), rgba(234, 88, 12, 0.3));
border-color: rgba(234, 88, 12, 0.4);
animation: pulse-orange 3s infinite;
}
.badge-pulse-red {
background: linear-gradient(135deg, rgba(239, 68, 68, 0.15), rgba(220, 38, 38, 0.3));
border-color: rgba(220, 38, 38, 0.4);
animation: pulse-red 3s infinite;
}
@keyframes pulse-green {
0% {
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.3);
}
70% {
box-shadow: 0 0 0 8px rgba(16, 185, 129, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
}
}
@keyframes pulse-yellow {
0% {
box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.3);
}
70% {
box-shadow: 0 0 0 8px rgba(245, 158, 11, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(245, 158, 11, 0);
}
}
@keyframes pulse-orange {
0% {
box-shadow: 0 0 0 0 rgba(249, 115, 22, 0.3);
}
70% {
box-shadow: 0 0 0 8px rgba(249, 115, 22, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(249, 115, 22, 0);
}
}
@keyframes pulse-red {
0% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.3);
}
70% {
box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
}
}
.group-size {
@apply bg-gradient-to-br from-gray-50 to-gray-100 p-3 rounded-lg shadow-sm;
}
.gaming-item {
@apply flex items-center bg-white shadow-sm rounded-lg p-3 border-l-2 transition-all duration-300;
}
.gaming-item:hover {
@apply shadow-md;
transform: scale(1.01);
}
.gaming-icon {
@apply w-9 h-9 flex items-center justify-center rounded-full mr-3 shadow-sm;
}
.progress-container {
@apply w-full bg-gray-200 rounded-full h-3 mr-3 flex-1 shadow-inner;
}
.progress-bar {
@apply h-3 rounded-full transition-all duration-500;
}
.risk-level-text {
@apply text-xs whitespace-nowrap min-w-[3.5rem] text-right font-semibold;
}
.security-tips {
@apply bg-gradient-to-br from-sky-50 to-indigo-100 rounded-xl p-4 shadow-sm border border-blue-200;
}
.tip-list {
@apply mt-3 space-y-2;
}
.tip-item {
@apply flex items-start text-sm text-gray-700 bg-white p-2 rounded-lg shadow-sm border border-gray-100;
}
</style>

View File

@@ -2,9 +2,15 @@
<div class="flex items-center justify-center card">
<div class="max-w-md w-full p-4">
<!-- 核验结果 -->
<h3 class="text-xl font-semibold text-gray-700 mb-4">婚姻核验结果</h3>
<h3 class="text-xl font-semibold text-gray-700 mb-4">
婚姻核验结果
</h3>
<div
:class="['mb-6 p-2 rounded-3xl text-lg font-medium text-center text-white shadow-lg', getStatusClass(data.status)]">
:class="[
'mb-6 p-2 rounded-3xl text-lg font-medium text-center text-white shadow-lg',
getStatusClass(data.status),
]"
>
{{ getStatusText(data.status) }}
</div>
<div class="text-sm text-gray-600">
@@ -25,35 +31,36 @@ const props = defineProps({
required: true,
},
});
const { data } = toRefs(props);
// 状态背景色和图标
const getStatusClass = (status) => {
const statusClassMapping = {
"0": "bg-yellow-500",
"1": "bg-green-600",
"2": "bg-red-500",
"3": "bg-blue-400",
0: "bg-yellow-500",
1: "bg-green-600",
2: "bg-red-500",
3: "bg-blue-400",
};
return statusClassMapping[status] || "bg-gray-500";
};
// 核验状态描述
const getStatusText = (status) => {
const statusMapping = {
"0": "无婚姻关系",
"1": "已婚",
"2": "已离婚",
"3": "离婚冷静期",
0: "无婚姻关系",
1: "已婚",
2: "已离婚",
3: "离婚冷静期",
};
return statusMapping[status] || "未知状态";
};
const statusDescription = (status) => {
const descriptionMapping = {
"1": "双方是夫妻关系,目前处于已婚状态。",
"2": "双方存在过婚姻关系,目前已离婚。",
"3": "双方目前处于离婚冷静期。",
"0": "双方不存在婚姻关系。",
1: "双方是夫妻关系,目前处于已婚状态。",
2: "双方存在过婚姻关系,目前已离婚。",
3: "双方目前处于离婚冷静期。",
0: "双方不存在婚姻关系。",
};
return descriptionMapping[status] || "无详细描述。";
};

931
src/ui/CG11BJ06.vue Normal file
View File

@@ -0,0 +1,931 @@
<script setup>
import { computed } from "vue";
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
});
onMounted(() => {
console.log("data", props.data);
});
// 格式化日期从YYMM格式转换为YYYY年MM月格式
const formatDate = (dateStr) => {
if (!dateStr || dateStr.length !== 4) return "未知";
const yearShort = dateStr.substring(0, 2);
const month = dateStr.substring(2, 4);
// 假设都是21世纪的年份
const fullYear = `20${yearShort}`;
return `${fullYear}${month}`;
};
// 获取学历等级对应的描述
const getEducationDesc = (education) => {
const descriptions = {
大学专科:
"专科学历是高等教育的重要组成部分,培养具有专业知识和技能的应用型人才。",
大学本科:
"本科学历是高等教育的基础学位,培养具有系统专业知识和基本技能的高级人才。",
硕士研究生:
"硕士学位是较高层次的学位,培养具有较深厚理论基础和专业技能的高级专门人才。",
博士研究生:
"博士学位是最高学位,培养能够独立从事科学研究工作、具有创新能力的高级专门人才。",
博士后: "博士后是在获得博士学位后进行的进一步研究和深造,是学术界的高级研究人员。",
};
return descriptions[education] || "";
};
// 根据学校类型获取不同的样式类
const getSchoolTypeClass = (type) => {
const classes = {
"985学校": "border-amber-500 bg-amber-50",
"211学校": "border-blue-500 bg-blue-50",
双一流学校: "border-green-500 bg-green-50",
其他: "border-gray-300 bg-gray-50",
};
return classes[type] || "border-gray-300 bg-gray-50";
};
// 根据学历等级获取时间线点的样式类
const getTimelinePointClass = (education) => {
const classes = {
大学专科: "bg-gray-500",
大学本科: "bg-blue-500",
硕士研究生: "bg-green-500",
博士研究生: "bg-amber-500",
博士后: "bg-amber-500",
};
return classes[education] || "bg-blue-500";
};
// 获取学校类型对应的标语
const getSchoolSlogan = (type) => {
if (type === "985学校") return "国家重点建设的高水平大学";
if (type === "211学校") return "面向21世纪重点建设的高等学校";
if (type === "双一流学校") return "世界一流大学和一流学科建设高校";
return "";
};
// 获取学历等级
const getEducationLevel = (education) => {
const levels = {
大学专科: 1,
大学本科: 2,
硕士研究生: 3,
博士研究生: 4,
博士后: 5,
};
return levels[education] || 0;
};
// 获取学历图标SVG根据学历类型返回不同的图标
const getEducationSvgIcon = (education) => {
// 默认图标 - 毕业帽
let svgIcon = `<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l9-5-9-5-9 5 9 5z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"></path>
</svg>`;
if (education === "大学本科") {
// 本科 - 文凭
svgIcon = `<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
</svg>`;
} else if (education === "硕士研究生") {
// 硕士 - 书和毕业帽
svgIcon = `<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path>
</svg>`;
} else if (education === "博士研究生" || education === "博士后") {
// 博士/博士后 - 灯泡(创新)
svgIcon = `<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"></path>
</svg>`;
}
return svgIcon;
};
// 计算排序后的学历数据
const sortedEducations = computed(() => {
const educationList = props.data?.data || [];
if (educationList.length <= 1) return educationList;
// 按照入学日期从早到晚排序
return [...educationList].sort((a, b) => {
// 假设ksrq是MMDD格式转为数字比较
const dateA = parseInt(a.ksrq || "0000");
const dateB = parseInt(b.ksrq || "0000");
return dateA - dateB;
});
});
// 判断是否有学历数据
const hasEducationData = computed(() => {
return props.data?.status === 1 && sortedEducations.value.length > 0;
});
// 判断是否有多个学历
const hasMultipleEducations = computed(() => {
return sortedEducations.value.length > 1;
});
</script>
<template>
<div
class="w-full max-w-md mx-auto bg-white rounded-xl overflow-hidden font-sans shadow-md"
>
<div v-if="hasEducationData" class="p-6 pl-2">
<!-- 学历时间线 -->
<div class="relative pb-4">
<!-- 垂直时间线 -->
<div
v-if="hasMultipleEducations"
class="absolute left-0 top-14 h-[calc(100%-44px)] w-0.5 bg-gradient-to-b from-blue-400 to-blue-200 rounded-full ml-5"
></div>
<!-- 学历卡片列表 -->
<div class="space-y-10 relative">
<div
v-for="(education, index) in sortedEducations"
:key="index"
class="relative"
>
<!-- 时间线点 -->
<div
v-if="hasMultipleEducations"
:class="[
'absolute left-5 w-10 h-10 rounded-full border-4 border-white shadow-md flex items-center justify-center text-white transform -translate-x-1/2',
getTimelinePointClass(education.xl),
]"
>
<span class="text-sm font-bold">{{
index + 1
}}</span>
</div>
<!-- 学历卡片 -->
<div
:class="[
'relative rounded-lg transition-all duration-300 ml-12',
'p-6 hover:-translate-y-1 hover:shadow-lg',
getSchoolTypeClass(education.xxlx),
]"
>
<!-- 顶部彩色条 -->
<div
:class="[
'absolute top-0 left-0 w-full h-1.5 rounded-t-lg',
education.xxlx === '985学校'
? 'bg-gradient-to-r from-amber-500 to-amber-300'
: education.xxlx === '211学校'
? 'bg-gradient-to-r from-blue-500 to-blue-300'
: education.xxlx === '双一流学校'
? 'bg-gradient-to-r from-green-500 to-green-300'
: 'bg-gradient-to-r from-gray-400 to-gray-300',
]"
></div>
<!-- 时间和学校类型标签 -->
<div
class="flex flex-col sm:flex-row sm:justify-between mb-5 pt-2"
>
<!-- 学校类型标签 -->
<div
v-if="education.xxlx !== '其他'"
class="bg-gradient-to-r from-yellow-400 to-yellow-500 text-white text-sm font-medium px-4 py-1.5 rounded-full shadow-sm inline-flex items-center mb-3 sm:mb-0 max-w-fit"
>
<svg
class="w-4 h-4 mr-1.5"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M6.267 3.455a3.066 3.066 0 001.745-.723 3.066 3.066 0 013.976 0 3.066 3.066 0 001.745.723 3.066 3.066 0 012.812 2.812c.051.643.304 1.254.723 1.745a3.066 3.066 0 010 3.976 3.066 3.066 0 00-.723 1.745 3.066 3.066 0 01-2.812 2.812 3.066 3.066 0 00-1.745.723 3.066 3.066 0 01-3.976 0 3.066 3.066 0 00-1.745-.723 3.066 3.066 0 01-2.812-2.812 3.066 3.066 0 00-.723-1.745 3.066 3.066 0 010-3.976 3.066 3.066 0 00.723-1.745 3.066 3.066 0 012.812-2.812zm7.44 5.252a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
></path>
</svg>
{{ education.xxlx }}
</div>
<!-- 时间信息 -->
<div
class="flex items-center text-gray-600 text-sm sm:text-base mt-1 sm:mt-0"
>
<svg
class="w-4 h-4 text-blue-500 mr-2 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
></path>
</svg>
<span
>{{ formatDate(education.ksrq) }} -
{{ formatDate(education.jsrq) }}</span
>
</div>
</div>
<!-- 学历标题区域 -->
<div class="flex items-start mb-6">
<div
class="w-14 h-14 rounded-full bg-blue-50 flex items-center justify-center mr-5 flex-shrink-0 text-blue-500"
>
<span
v-html="
getEducationSvgIcon(education.xl)
"
></span>
</div>
<div class="flex-1">
<div
class="flex justify-between items-start"
>
<h3
class="text-xl font-semibold text-gray-800 mb-1"
>
{{ education.xl }}
</h3>
<!-- 高级学历标签 -->
<div
v-if="
getEducationLevel(
education.xl
) >= 4
"
class="bg-red-500 text-white text-xs font-bold px-2.5 py-1 rounded-md shadow ml-2 animate-pulse"
>
顶级
</div>
</div>
<p
v-if="education.xxlx !== '其他'"
class="text-sm text-yellow-600 italic"
>
"{{ getSchoolSlogan(education.xxlx) }}"
</p>
</div>
</div>
<!-- 分隔线 -->
<div class="w-full h-px bg-gray-200 my-5"></div>
<!-- 学历详情 -->
<div class="space-y-4 mb-6">
<!-- 入学和毕业时间(改为两行布局) -->
<!-- 入学时间 -->
<div class="flex items-center text-gray-700">
<svg
class="w-5 h-5 text-green-500 mr-3 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
></path>
</svg>
<span
class="text-gray-500 mr-3 text-sm sm:text-base"
>入学时间:</span
>
<span
class="text-gray-700 text-sm sm:text-base font-medium"
>{{ formatDate(education.ksrq) }}</span
>
</div>
<!-- 毕业时间 -->
<div class="flex items-center text-gray-700">
<svg
class="w-5 h-5 text-red-500 mr-3 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
></path>
</svg>
<span
class="text-gray-500 mr-3 text-sm sm:text-base"
>毕业时间:</span
>
<span
class="text-gray-700 text-sm sm:text-base font-medium"
>{{ formatDate(education.jsrq) }}</span
>
</div>
<!-- 专业 -->
<div class="flex items-center text-gray-700">
<svg
class="w-5 h-5 text-blue-400 mr-3 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
></path>
</svg>
<span
class="text-gray-500 mr-3 text-sm sm:text-base"
>专业:</span
>
<span
class="text-gray-700 text-sm sm:text-base"
>{{ education.zymc }}</span
>
</div>
<!-- 学习方式 -->
<div class="flex items-center text-gray-700">
<svg
class="w-5 h-5 text-blue-400 mr-3 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
<span
class="text-gray-500 mr-3 text-sm sm:text-base"
>学习方式:</span
>
<span
class="text-gray-700 text-sm sm:text-base"
>{{ education.xxxs }}</span
>
</div>
</div>
<!-- 学历描述 -->
<div
class="relative overflow-hidden rounded-lg mt-5"
>
<!-- 背景效果 -->
<div
class="absolute inset-0 bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg"
></div>
<!-- 装饰性元素 - 只在高级学历显示 -->
<div
v-if="getEducationLevel(education.xl) >= 4"
class="absolute -right-4 -top-4 w-16 h-16 bg-yellow-100 rounded-full opacity-50"
></div>
<div
v-if="getEducationLevel(education.xl) >= 4"
class="absolute -left-4 -bottom-4 w-12 h-12 bg-blue-100 rounded-full opacity-50"
></div>
<!-- 内容 -->
<div class="relative z-10 p-5">
<h4
class="font-medium text-base text-gray-700 mb-2 flex items-center"
>
<span class="mr-2"
>{{ education.xl }}学历</span
>
<span
v-if="
getEducationLevel(
education.xl
) >= 4
"
class="text-xs bg-yellow-400 text-white px-2 py-0.5 rounded"
>高级</span
>
</h4>
<p
class="text-gray-600 text-sm leading-relaxed"
>
{{ getEducationDesc(education.xl) }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 无数据状态 -->
<div
v-else
class="flex flex-col items-center py-10 px-5 text-center bg-gray-50"
>
<svg
class="w-16 h-16 text-gray-400 mb-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
></path>
</svg>
<div class="text-gray-600 text-sm max-w-md">
暂无学历信息记录。这可能是因为:
<ul class="text-left mt-3 pl-5">
<li
class="mb-2 relative before:content-['•'] before:absolute before:left-[-15px] before:text-blue-500"
>
学历信息不公开
</li>
<li
class="mb-2 relative before:content-['•'] before:absolute before:left-[-15px] before:text-blue-500"
>
暂无高等教育学历
</li>
<li
class="mb-2 relative before:content-['•'] before:absolute before:left-[-15px] before:text-blue-500"
>
学历较早暂未被教育部门数字化收录
</li>
</ul>
</div>
</div>
</div>
</template>
<style scoped>
.education-history {
padding: 24px;
border-radius: 16px;
background-color: #fff;
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08);
font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
position: relative;
overflow: hidden;
}
.education-history::before {
content: "";
position: absolute;
top: 0;
right: 0;
width: 150px;
height: 150px;
background: radial-gradient(
circle at top right,
rgba(64, 158, 255, 0.05),
transparent 70%
);
border-radius: 0 0 0 100%;
z-index: 0;
}
.education-content {
padding: 10px 0;
position: relative;
z-index: 1;
}
.decoration-top {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 30px;
}
.decoration-line {
height: 1px;
background: linear-gradient(
to right,
transparent,
rgba(64, 158, 255, 0.5),
transparent
);
flex-grow: 1;
}
.decoration-icon {
font-size: 22px;
margin: 0 15px;
background-color: #f0f7ff;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
}
.decoration-bottom {
display: flex;
justify-content: center;
margin-top: 20px;
}
.decoration-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #409eff;
margin: 0 3px;
opacity: 0.5;
}
.timeline {
position: relative;
padding-left: 20px;
}
.timeline-item {
position: relative;
padding-bottom: 40px;
}
.timeline-item:last-child {
padding-bottom: 0;
}
.timeline-point {
position: absolute;
left: -10px;
top: 0;
width: 20px;
height: 20px;
border-radius: 50%;
background-color: #409eff;
border: 4px solid #fff;
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.3);
z-index: 2;
transition: all 0.3s ease;
}
.edu-associate .timeline-point {
background-color: #909399;
}
.edu-bachelor .timeline-point {
background-color: #409eff;
}
.edu-master .timeline-point {
background-color: #67c23a;
}
.edu-doctor .timeline-point,
.edu-postdoc .timeline-point {
background-color: #ff9900;
}
.timeline-item:hover .timeline-point {
transform: scale(1.2);
}
.timeline-line {
position: absolute;
left: 0;
top: 20px;
height: calc(100% - 20px);
width: 2px;
background: linear-gradient(to bottom, #409eff, #67c23a);
z-index: 1;
}
.education-card {
margin-left: 20px;
padding: 20px;
border-radius: 12px;
background-color: #f5f7fa;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
border-left: none;
}
.education-card::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: linear-gradient(to right, #409eff, #67c23a);
opacity: 0.7;
}
.school-985::after {
background: linear-gradient(to right, #ff9900, #ffba56);
}
.school-211::after {
background: linear-gradient(to right, #409eff, #6ac0ff);
}
.school-double-first-class::after {
background: linear-gradient(to right, #67c23a, #95d475);
}
.education-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
.card-corner {
position: absolute;
bottom: 0;
right: 0;
width: 30px;
height: 30px;
background: linear-gradient(
to bottom right,
transparent 49%,
rgba(64, 158, 255, 0.1) 50%
);
}
.school-985 .card-corner {
background: linear-gradient(
to bottom right,
transparent 49%,
rgba(255, 153, 0, 0.1) 50%
);
}
.school-211 .card-corner {
background: linear-gradient(
to bottom right,
transparent 49%,
rgba(64, 158, 255, 0.1) 50%
);
}
.school-double-first-class .card-corner {
background: linear-gradient(
to bottom right,
transparent 49%,
rgba(103, 194, 58, 0.1) 50%
);
}
.education-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.education-time {
font-size: 16px;
color: #606266;
display: flex;
align-items: center;
}
.time-icon {
margin-right: 5px;
font-style: normal;
}
.education-level {
font-size: 18px;
font-weight: bold;
color: #303133;
padding: 4px 12px;
border-radius: 30px;
background-color: rgba(0, 0, 0, 0.03);
}
.education-details {
margin-bottom: 16px;
position: relative;
}
.school-type-badge {
display: inline-flex;
align-items: center;
padding: 6px 12px;
margin-bottom: 12px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
background-color: #409eff;
color: white;
box-shadow: 0 2px 6px rgba(64, 158, 255, 0.3);
}
.badge-icon {
margin-right: 5px;
}
.school-985 .school-type-badge {
background-color: #ff9900;
box-shadow: 0 2px 6px rgba(255, 153, 0, 0.3);
}
.school-211 .school-type-badge {
background-color: #409eff;
box-shadow: 0 2px 6px rgba(64, 158, 255, 0.3);
}
.school-double-first-class .school-type-badge {
background-color: #67c23a;
box-shadow: 0 2px 6px rgba(103, 194, 58, 0.3);
}
.education-major,
.education-mode {
margin-bottom: 10px;
font-size: 15px;
color: #606266;
display: flex;
align-items: center;
}
.label {
color: #909399;
margin-right: 8px;
display: flex;
align-items: center;
}
.major-icon,
.mode-icon {
font-style: normal;
margin-right: 5px;
}
.value {
color: #303133;
font-weight: 500;
}
.education-description {
margin-top: 16px;
padding: 14px;
background-color: rgba(0, 0, 0, 0.02);
border-radius: 8px;
border-left: 3px solid rgba(64, 158, 255, 0.3);
}
.school-985 .education-description {
border-left: 3px solid rgba(255, 153, 0, 0.3);
}
.school-211 .education-description {
border-left: 3px solid rgba(64, 158, 255, 0.3);
}
.school-double-first-class .education-description {
border-left: 3px solid rgba(103, 194, 58, 0.3);
}
.desc-title {
font-size: 15px;
font-weight: bold;
margin-bottom: 8px;
color: #303133;
}
.desc-content {
font-size: 14px;
color: #606266;
line-height: 1.6;
}
/* 不同学历等级的样式 */
.edu-associate .education-level {
color: #909399;
background-color: rgba(144, 147, 153, 0.1);
}
.edu-bachelor .education-level {
color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.edu-master .education-level {
color: #67c23a;
background-color: rgba(103, 194, 58, 0.1);
}
.edu-doctor .education-level,
.edu-postdoc .education-level {
color: #ff9900;
background-color: rgba(255, 153, 0, 0.1);
position: relative;
}
.edu-doctor .education-level:after,
.edu-postdoc .education-level:after {
content: "🎓";
position: absolute;
right: -24px;
top: -2px;
}
.edu-postdoc .education-level:after {
content: "🏆";
}
/* 无数据样式 */
.no-data {
display: flex;
flex-direction: column;
align-items: center;
padding: 40px 20px;
text-align: center;
background: linear-gradient(to bottom, #ffffff, #f5f7fa);
border-radius: 12px;
}
.no-data-icon {
font-size: 54px;
margin-bottom: 20px;
opacity: 0.6;
background: linear-gradient(to bottom, #409eff, #67c23a);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.no-data-text {
color: #606266;
font-size: 15px;
max-width: 400px;
}
.no-data-text ul {
text-align: left;
margin-top: 10px;
padding-left: 20px;
}
.no-data-text li {
margin-bottom: 8px;
position: relative;
}
.no-data-text li:before {
content: "•";
position: absolute;
left: -15px;
color: #409eff;
}
/* 适配移动端 */
@media screen and (max-width: 768px) {
.education-history {
padding: 16px;
}
.education-card {
margin-left: 10px;
padding: 16px;
}
.timeline-point {
left: -8px;
width: 16px;
height: 16px;
}
.education-header {
flex-direction: column;
align-items: flex-start;
}
.education-level {
margin-top: 8px;
}
}
</style>

320
src/ui/CG29BJ05.vue Normal file
View File

@@ -0,0 +1,320 @@
<script setup>
import { computed } from "vue";
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
});
// 计算得分如果没有数据则默认为0
const score = computed(() => {
return props.data?.score ? Number(props.data.score) : 0;
});
// 根据分值确定压力等级
const pressureLevel = computed(() => {
if (score.value <= 20)
return {
level: "低",
color: "#67C23A",
text: "还款压力小",
bgGradient: "from-green-500 to-green-300",
lightBg: "bg-green-50",
borderColor: "border-green-200",
icon: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.828 14.828a4 4 0 01-5.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`,
};
if (score.value <= 50)
return {
level: "中",
color: "#E6A23C",
text: "还款压力中等",
bgGradient: "from-yellow-500 to-yellow-300",
lightBg: "bg-yellow-50",
borderColor: "border-yellow-200",
icon: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`,
};
if (score.value <= 80)
return {
level: "高",
color: "#F56C6C",
text: "还款压力较大",
bgGradient: "from-orange-500 to-red-400",
lightBg: "bg-red-50",
borderColor: "border-red-200",
icon: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>`,
};
return {
level: "极高",
color: "#FF0000",
text: "还款压力非常大",
bgGradient: "from-red-600 to-red-500",
lightBg: "bg-red-50",
borderColor: "border-red-300",
icon: `<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>`,
};
});
// 计算进度条宽度百分比
const progressWidth = computed(() => {
return `${score.value}%`;
});
// 计算评分对应的Tailwind文本颜色类
const scoreColorClass = computed(() => {
if (score.value <= 20) return "text-green-500";
if (score.value <= 50) return "text-yellow-500";
if (score.value <= 80) return "text-orange-500";
return "text-red-600";
});
</script>
<template>
<div
class="p-6 rounded-xl bg-white shadow-lg border border-gray-100 transition-all duration-300 hover:shadow-xl relative overflow-hidden font-sans"
>
<!-- 装饰背景元素 -->
<div
class="absolute -right-10 -top-10 w-40 h-40 bg-gray-50 rounded-full opacity-30"
></div>
<div
class="absolute -left-12 -bottom-12 w-36 h-36 bg-gray-50 rounded-full opacity-20"
></div>
<!-- 标题栏 -->
<div class="flex justify-between items-center mb-6 relative z-10">
<h3
class="text-lg font-bold text-gray-800 tracking-wide flex items-center"
>
<span
class="bg-gradient-to-r from-blue-500 to-blue-600 w-1.5 h-5 rounded mr-2 inline-block"
></span>
还款压力分析
</h3>
<div
class="px-3 py-1 bg-blue-50 rounded-full text-xs text-blue-500 font-medium"
>
借贷风险评估
</div>
</div>
<!-- 分数显示 - 无仪表盘设计 -->
<div class="flex flex-col items-center justify-center mb-6">
<!-- 分数圆环 -->
<div
class="relative h-32 w-32 flex items-center justify-center mb-2"
>
<!-- 背景圆环 -->
<div
class="absolute inset-0 rounded-full border-[8px] border-gray-100"
></div>
<!-- 彩色进度圆环 - 根据分数变化颜色 -->
<svg
class="absolute inset-0 w-full h-full -rotate-90"
viewBox="0 0 100 100"
>
<circle
cx="50"
cy="50"
r="46"
fill="none"
:stroke="pressureLevel.color"
stroke-width="8"
stroke-dasharray="289.02"
:stroke-dashoffset="
289.02 - (289.02 * score.value) / 100
"
stroke-linecap="round"
class="transition-all duration-700 ease-out"
/>
</svg>
<!-- 分数显示 -->
<div class="flex flex-col items-center justify-center z-10">
<span :class="['text-5xl font-bold', scoreColorClass]">{{
score
}}</span>
<span class="text-sm text-gray-500 mt-1">得分</span>
</div>
</div>
<!-- 等级标签 -->
<div
class="inline-flex items-center px-4 py-1.5 rounded-full shadow-sm mb-1"
:class="[pressureLevel.lightBg, pressureLevel.borderColor]"
>
<span class="mr-1.5" v-html="pressureLevel.icon"></span>
<span
class="font-medium"
:style="{ color: pressureLevel.color }"
>
{{ pressureLevel.level }}级还款压力
</span>
</div>
</div>
<!-- 进度条 -->
<div class="mb-6">
<div class="h-2.5 bg-gray-100 rounded-full overflow-hidden mb-2">
<div
class="h-full rounded-full transition-all duration-500 ease-out bg-gradient-to-r"
:class="pressureLevel.bgGradient"
:style="{ width: progressWidth }"
></div>
</div>
<div class="flex justify-between text-xs text-gray-400">
<span class="font-medium">0</span>
<span>25</span>
<span>50</span>
<span>75</span>
<span class="font-medium">100</span>
</div>
</div>
<!-- 压力等级显示 -->
<div class="flex justify-center mb-6">
<div
class="py-3 px-5 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-lg border border-blue-100 text-center"
>
<span class="text-gray-700">{{ pressureLevel.text }}</span>
<div class="mt-1 text-xs text-gray-500">
分值越高表示还款压力越大建议关注债务比例
</div>
</div>
</div>
<!-- 建议区域 -->
<div class="bg-gray-50 rounded-lg p-4 border border-gray-100 relative">
<div
class="absolute -top-3 left-4 bg-white px-2 text-xs font-medium text-gray-500"
>
财务建议
</div>
<div class="flex items-start">
<div class="mt-0.5 mr-3 text-blue-500">
<svg
class="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
></path>
</svg>
</div>
<div>
<p v-if="score > 50" class="text-gray-600 leading-relaxed">
建议合理规划财务控制债务比例增加收入来源避免过度负债
<span class="block mt-1 text-sm text-gray-500"
>可尝试分期付款或延长还款周期减轻每月还款压力</span
>
</p>
<p v-else class="text-gray-600 leading-relaxed">
当前还款压力在可控范围内继续保持良好的财务习惯
<span class="block mt-1 text-sm text-gray-500"
>建议定期检查收支平衡确保及时还款维持良好信用记录</span
>
</p>
</div>
</div>
</div>
</div>
</template>
<style scoped>
.repayment-pressure {
padding: 20px;
border-radius: 12px;
background-color: #fff;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
font-family: "PingFang SC", "Helvetica Neue", Arial, sans-serif;
}
.title {
font-size: 18px;
font-weight: bold;
margin-bottom: 16px;
color: #303133;
}
.score-display {
text-align: center;
margin-bottom: 20px;
}
.score-value {
font-size: 40px;
font-weight: bold;
}
.score-unit {
font-size: 16px;
margin-left: 4px;
color: #606266;
}
.progress-container {
margin-bottom: 20px;
}
.progress-bar {
height: 10px;
background-color: #ebeef5;
border-radius: 10px;
overflow: hidden;
margin-bottom: 4px;
}
.progress-fill {
height: 100%;
border-radius: 10px;
transition: width 0.5s ease, background-color 0.5s ease;
}
.progress-markers {
display: flex;
justify-content: space-between;
color: #909399;
font-size: 12px;
}
.pressure-level {
font-size: 16px;
font-weight: bold;
margin-bottom: 16px;
text-align: center;
}
.suggestion {
padding: 10px;
background-color: #f8f9fa;
border-radius: 6px;
color: #606266;
font-size: 14px;
}
.suggestion p {
margin: 0;
}
/* 适配移动端 */
@media screen and (max-width: 768px) {
.repayment-pressure {
padding: 16px;
}
.score-value {
font-size: 32px;
}
.title {
font-size: 16px;
}
}
</style>

View File

@@ -4,11 +4,11 @@ import LTitle from "@/components/LTitle.vue";
const props = defineProps({
data: {
type: Object,
required: true,
required: false,
default: () => null,
},
});
const { data } = props;
// 状态映射,包括显示的文字和样式
const statusMap = {
0: {
@@ -37,28 +37,35 @@ const statusMap = {
},
};
// 根据 `data.status` 确定当前状态,默认值为 “无相关记录”
const currentStatus =
data.status !== undefined
? statusMap[data.status] || statusMap["0"]
: {
text: "相关记录",
bgClass: "bg-gray-200",
textClass: "text-gray-500",
description: "暂无婚姻相关记录",
};
// 无记录时的状态
const noRecordStatus = {
text: "无相关记录",
bgClass: "bg-gray-200",
textClass: "text-gray-500",
description: "暂无婚姻相关记录",
// description: "可能由于民政部门系统正在升级,当前查无相关记录。<br />1. 当查询到相关结果时,我们将第一时间更新报告(您可以在我的历史查询记录中查看),并通过短信通知您。<br />2. 如果10个工作日内未能查询到相关信息我们承诺将为您退款26.8元的婚姻状态查询费用。",
};
// 根据 `data.status` 确定当前状态,默认值为 "无相关记录"
const currentStatus = !data
? noRecordStatus
: data.status !== undefined
? statusMap[data.status] || statusMap["0"]
: noRecordStatus;
</script>
<template>
<div class="card">
<div class="status-info flex flex-col items-center ">
<div class="status-info flex flex-col items-center">
<div
:class="`status-label rounded-full px-6 py-3 text-center font-bold shadow-md ${currentStatus.bgClass} ${currentStatus.textClass}`">
:class="`status-label rounded-full px-6 py-3 text-center font-bold shadow-md ${currentStatus.bgClass} ${currentStatus.textClass}`"
>
{{ currentStatus.text }}
</div>
<p class="status-description mt-3 text-sm text-gray-600">
{{ currentStatus.description }}
</p>
<p
v-html="currentStatus.description"
class="status-description mt-3 text-sm text-gray-600"
></p>
</div>
</div>
</template>

View File

@@ -80,6 +80,11 @@ const providerColor = computed(() => {
return colorMap[props.data.provider] || '#10b981';
});
onMounted(() => {
console.log('CMOB035');
console.log(props.data);
});
</script>
<template>

View File

@@ -4,7 +4,8 @@ import LTitle from "@/components/LTitle.vue";
const props = defineProps({
data: {
type: Object,
required: true,
required: false,
default: () => null
},
});
const { data } = props;
@@ -37,21 +38,22 @@ const statusMap = {
},
};
// 根据 `data.status` 确定当前状态,默认值为 “无相关记录”
const currentStatus =
data.status !== undefined
? statusMap[data.status] || statusMap["0"]
: {
text: "相关记录",
bgClass: "bg-gray-200",
textClass: "text-gray-500",
description: "暂无婚姻相关记录",
};
// 无记录时的状态
const noRecordStatus = {
text: "无相关记录",
bgClass: "bg-gray-200",
textClass: "text-gray-500",
description: "暂无婚姻相关记录",
};
// 根据 `data.status` 确定当前状态,默认值为 "无相关记录"
const currentStatus = !data ? noRecordStatus :
(data.status !== undefined ? statusMap[data.status] || statusMap["0"] : noRecordStatus);
</script>
<template>
<div class="card">
<div class="status-info flex flex-col items-center ">
<div class="status-info flex flex-col items-center">
<div
:class="`status-label rounded-full px-6 py-3 text-center font-bold shadow-md ${currentStatus.bgClass} ${currentStatus.textClass}`">
{{ currentStatus.text }}

View File

@@ -0,0 +1,85 @@
<script setup>
import Investment from "./sections/Investment.vue";
import SeniorExecutive from "./sections/SeniorExecutive.vue";
import Lawsuit from "./sections/Lawsuit.vue";
const props = defineProps({
data: {
type: Object,
required: true,
default: () => ({}),
},
});
const { data } = props;
// 投资类关系
const investRelations = ["sh", "his_sh", "lp", "his_lp"];
// 高管类关系
const managerRelations = ["tm", "his_tm"];
const totalCompanies = computed(() => {
return data?.datalist?.length || 0;
});
// 获取投资企业记录(股东、历史股东、法人、历史法人)
const investCompanies = computed(() => {
const datalist = data?.datalist || [];
return datalist.filter((item) => {
const relationships = item?.relationship || [];
return relationships.some((r) => investRelations.includes(r));
});
});
// 获取高管任职记录(高管、历史高管)
const managerPositions = computed(() => {
const datalist = data?.datalist || [];
return datalist.filter((item) => {
const relationships = item?.relationship || [];
return relationships.some((r) => managerRelations.includes(r));
});
});
// 获取有涉诉风险的企业
const lawsuitCompanies = computed(() => {
const datalist = data?.datalist || [];
return datalist.filter((item) => {
const lawsuit = item?.lawsuitInfo || {};
return (
(lawsuit.data && Object.keys(lawsuit.data).length > 0) ||
(lawsuit.sxbzxr && lawsuit.sxbzxr.length > 0) ||
(lawsuit.xgbzxr && lawsuit.xgbzxr.length > 0)
);
});
});
</script>
<template>
<div class="pb-8">
<!-- 投资企业记录 -->
<Investment :data="investCompanies" />
<!-- 高管任职记录 -->
<SeniorExecutive :data="managerPositions" />
<!-- 涉诉风险 -->
<Lawsuit :data="lawsuitCompanies" :total-companies="totalCompanies" />
</div>
</template>
<style scoped>
/* 添加过渡效果 */
.company-wrapper {
overflow: hidden;
}
/* 添加阴影悬停效果 */
.company-wrapper > div:first-child {
transform-origin: top;
}
/* 确保展开有足够的空间 */
.max-h-\[2000px\] {
max-height: 2000px;
}
</style>

View File

@@ -0,0 +1,499 @@
<template>
<div class="mb-8 card shadow-sm rounded-xl overflow-hidden">
<LTitle title="投资企业记录" type="blue-green" class="mb-4" />
<div v-if="investCompanies.length > 0" class="space-y-3">
<div v-for="(company, index) in investCompanies" :key="index" class="company-wrapper">
<!-- 企业卡片 - 可点击展开 -->
<div
class="bg-white rounded-lg overflow-hidden shadow-sm border border-gray-200 hover:border-blue-300 hover:shadow-md transition-all duration-300 cursor-pointer relative"
:class="{
'border-blue-400 shadow-md': isCompanyExpanded(company.orgName, 'invest', index),
}"
@click="toggleCompanyExpand(company.orgName, 'invest', index)"
>
<!-- 企业名称 - 蓝色渐变背景 -->
<div class="bg-gradient-to-r from-blue-50 to-blue-100 px-4 py-2 flex items-center">
<div class="font-medium text-gray-800 truncate">{{ company.orgName }}</div>
</div>
<!-- 关系标签 - 靠左排列无背景 -->
<div class="px-4 py-1.5 flex items-center">
<div class="flex flex-wrap gap-1">
<span
v-for="(relation, idx) in company.relationship"
:key="idx"
class="px-1.5 py-0.5 text-xs rounded-full"
:class="getRelationshipClass(relation)"
>
{{ getRelationshipText(relation) }}
</span>
</div>
</div>
<!-- 企业状态注册资本注册时间 -->
<div
class="px-4 py-2 flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2 text-xs text-gray-500"
>
<div class="flex items-center gap-3">
<span
v-if="company.basicInfo && company.basicInfo.regStatus"
class="px-1.5 py-0.5 rounded-full inline-block"
:class="getStatusClass(company.basicInfo.regStatus)"
>
{{ company.basicInfo.regStatus }}
</span>
<span v-if="company.basicInfo && company.basicInfo.regCapital">
{{ formatCapital(company.basicInfo.regCapital, company.basicInfo.regCapitalCurrency) }}
</span>
</div>
<div>
<span v-if="company.basicInfo && company.basicInfo.estiblishTime">
{{ formatDate(company.basicInfo.estiblishTime) }}
</span>
</div>
</div>
<!-- 展开指示器 - 放在右下角 -->
<div
class="absolute right-4 bottom-2 flex items-center text-xs text-gray-500 hover:text-blue-600 transition-colors duration-300"
:class="{
'text-blue-600': isCompanyExpanded(company.orgName, 'invest', index),
}"
>
<span class="mr-1">{{
isCompanyExpanded(company.orgName, 'invest', index) ? '收起详情' : '查看详情'
}}</span>
<div
class="w-5 h-5 rounded-full bg-gray-100 flex items-center justify-center transition-transform duration-300"
:class="{
'bg-blue-100 text-blue-600 rotate-180': isCompanyExpanded(company.orgName, 'invest', index),
}"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
</div>
<!-- 企业详情抽屉 - 投资企业记录 -->
<div
class="overflow-hidden transition-all duration-500 ease-in-out"
:class="{
'max-h-0 opacity-0': !isCompanyExpanded(company.orgName, 'invest', index),
'max-h-[500px] opacity-100': isCompanyExpanded(company.orgName, 'invest', index),
}"
>
<div
class="mt-1 bg-gray-50 rounded-lg overflow-hidden p-4 pb-0 border border-gray-200 transform transition-all duration-300 max-h-[500px] overflow-y-auto"
>
<div class="relative">
<!-- 持股信息 - 如果有的话 -->
<div
v-if="company.stockHolderItem && Object.keys(company.stockHolderItem).length > 0"
class="mb-6 relative"
>
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center mr-3">
💼
</div>
<div class="text-base font-medium text-blue-700">持股信息</div>
</div>
<div class="pl-10">
<div class="bg-blue-50 p-4 rounded-lg border border-blue-100">
<div class="bg-white p-3 rounded-lg shadow-sm border border-blue-100">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<div class="text-xs text-gray-500 mb-1">股东名称</div>
<div class="text-sm font-medium text-gray-800">
{{ company.stockHolderItem.orgHolderName || '—' }}
</div>
</div>
<div>
<div class="text-xs text-gray-500 mb-1">股东类型</div>
<div class="text-sm text-gray-800">
{{ company.stockHolderItem.orgHolderType || '—' }}
</div>
</div>
<div>
<div class="text-xs text-gray-500 mb-1">出资金额</div>
<div class="text-sm text-gray-800">
{{ formatSubscriptAmt(company.stockHolderItem.subscriptAmt) }}
</div>
</div>
<div>
<div class="text-xs text-gray-500 mb-1">持股比例</div>
<div class="flex flex-col">
<div class="flex items-center">
<span class="text-sm font-medium text-blue-700">{{
formatRate(company.stockHolderItem.investRate)
}}</span>
<span
v-if="parseFloat(company.stockHolderItem.investRate) > 0"
class="ml-2 text-xs px-1.5 py-0.5 rounded-full bg-blue-50 text-blue-600"
>
{{ getInvestRateDesc(company.stockHolderItem.investRate) }}
</span>
</div>
<div class="mt-1.5 w-full h-2 bg-gray-100 rounded-full overflow-hidden">
<div
class="h-full rounded-full"
:class="getInvestRateClass(company.stockHolderItem.investRate)"
:style="{
width: `${Math.min(parseFloat(company.stockHolderItem.investRate) || 0, 100)}%`,
}"
></div>
</div>
</div>
</div>
<div>
<div class="text-xs text-gray-500 mb-1">出资时间</div>
<div class="text-sm text-gray-800">
{{ formatDate(company.stockHolderItem.investDate) }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 垂直连接线 -->
<div class="absolute left-0 top-8 bottom-0 w-0.5 bg-gray-200 ml-3"></div>
<!-- 分组信息展示 -->
<div v-if="company.basicInfo" class="space-y-6">
<div v-for="(group, groupIdx) in groupCompanyInfo(company)" :key="groupIdx" class="relative">
<!-- 分组标题 -->
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center mr-3">
{{ group.icon }}
</div>
<div class="text-base font-medium text-gray-700">
{{ group.title }}
</div>
</div>
<!-- 分组内容 -->
<div class="pl-10 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div
v-for="(field, fieldIdx) in group.fields"
:key="fieldIdx"
class="bg-white rounded-lg p-3 shadow-sm"
>
<div class="text-xs text-gray-500 mb-1">
{{ field.label }}
</div>
<div
v-if="field.type === 'status'"
class="text-sm px-2 py-0.5 rounded-full inline-block"
:class="getStatusClass(field.value)"
>
{{ field.value || '—' }}
</div>
<div v-else class="text-sm font-medium text-gray-800 flex items-center">
<span v-if="field.icon" class="mr-1">{{ field.icon }}</span>
{{ field.value || '' }}
</div>
</div>
</div>
</div>
</div>
<!-- 经营范围 -->
<div v-if="company.basicInfo && company.basicInfo.opscope" class="mt-6 relative">
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center mr-3">
📝
</div>
<div class="text-base font-medium text-gray-700">经营范围</div>
</div>
<div class="pl-10">
<div class="text-sm bg-white p-4 rounded-lg shadow-sm border border-gray-100 leading-relaxed">
{{ company.basicInfo.opscope }}
</div>
</div>
</div>
<!-- 主要人员 -->
<div
v-if="company.staffList && company.staffList.result && company.staffList.result.length > 0"
class="mt-6 relative"
>
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center mr-3">
👥
</div>
<div class="text-base font-medium text-gray-700">主要人员</div>
</div>
<div class="pl-10">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
<div
v-for="(staff, staffIdx) in company.staffList.result"
:key="staffIdx"
class="flex items-center bg-white p-3 rounded-lg shadow-sm border border-gray-100 hover:border-blue-200 transition-all"
>
<div
class="w-10 h-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-500 flex items-center justify-center text-white mr-3 text-sm font-medium"
>
{{ staff.name.substring(0, 1) }}
</div>
<div>
<div class="text-sm font-medium text-gray-800">
{{ staff.name }}
</div>
<div class="flex flex-wrap gap-1 mt-1">
<span
v-for="(position, posIdx) in staff.typeJoin || []"
:key="posIdx"
class="text-xs px-2 py-0.5 bg-blue-50 text-blue-600 rounded-full"
>
{{ position }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 底部收起按钮 -->
<div class="flex justify-center sticky bottom-0 pt-2 pb-2 bg-gradient-to-t from-gray-50 to-transparent">
<button
@click.stop="toggleCompanyExpand(company.orgName, 'invest', index)"
class="flex items-center px-4 py-2 rounded-full bg-blue-50 text-blue-600 hover:bg-blue-100 transition-colors duration-300"
>
<span class="mr-1">收起详情</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 transform rotate-180"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="text-gray-500 py-10 text-center bg-gray-50 rounded-lg mx-4 mb-4">
<div class="text-gray-300 text-3xl mb-2">📊</div>
暂无投资企业记录
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
})
const investCompanies = computed(() => props.data || [])
// 用于跟踪展开的企业卡片
const expandedCompanies = ref({})
// 切换展开/收起企业详情
const toggleCompanyExpand = (companyId, listType, index) => {
// 创建唯一标识符,包含企业名称、列表类型和索引
const uniqueKey = `${companyId}_${listType}_${index}`
expandedCompanies.value[uniqueKey] = !expandedCompanies.value[uniqueKey]
}
// 检查企业是否展开
const isCompanyExpanded = (companyId, listType, index) => {
const uniqueKey = `${companyId}_${listType}_${index}`
return !!expandedCompanies.value[uniqueKey]
}
// 获取关系文本
const getRelationshipText = relation => {
return relationshipMap[relation]?.text || relation
}
// 获取关系样式类
const getRelationshipClass = relation => {
return relationshipMap[relation]?.color || ''
}
// 企业信息分组函数
const groupCompanyInfo = company => {
if (!company || !company.basicInfo) return []
const basicInfo = company.basicInfo
return [
{
title: '基本信息',
icon: '🏢',
fields: [
{ label: '企业状态', value: basicInfo.regStatus, type: 'status' },
{ label: '统一社会信用代码', value: basicInfo.creditCode },
{ label: '注册号', value: basicInfo.regNumber },
{ label: '企业类型', value: basicInfo.entType },
{ label: '行业', value: basicInfo.industry },
{
label: '注册资本',
value: formatCapital(basicInfo.regCapital, basicInfo.regCapitalCurrency),
},
{ label: '成立日期', value: formatDate(basicInfo.estiblishTime), icon: '📅' },
{ label: '登记机关', value: basicInfo.regInstitute },
{ label: '核准日期', value: formatDate(basicInfo.approvedTime) },
],
},
{
title: '联系方式',
icon: '📞',
fields: [
{ label: '电话', value: basicInfo.phone },
{ label: '邮箱', value: basicInfo.email },
{ label: '网址', value: basicInfo.website },
{ label: '注册地址', value: basicInfo.regAddress },
],
},
{
title: '法人信息',
icon: '👨‍⚖️',
fields: [
{ label: '法人名称', value: basicInfo.legalPersonName },
{ label: '法人类型', value: basicInfo.legalPersonType },
],
},
]
}
// 关系映射表
const relationshipMap = {
sh: { text: '股东', color: 'bg-blue-100 text-blue-700', icon: '👑' },
his_sh: { text: '曾任股东', color: 'bg-blue-50 text-blue-600', icon: '🕒👑' },
lp: { text: '法人', color: 'bg-green-100 text-green-700', icon: '⚖️' },
his_lp: { text: '曾任法人', color: 'bg-green-50 text-green-600', icon: '🕒⚖️' },
tm: { text: '高管', color: 'bg-purple-100 text-purple-700', icon: '👔' },
his_tm: { text: '曾任高管', color: 'bg-purple-50 text-purple-600', icon: '🕒👔' },
}
// 获取企业状态对应的样式
const getStatusClass = status => {
if (!status) return 'bg-gray-100 text-gray-500'
if (status.includes('注销') || status.includes('吊销')) {
return 'bg-red-50 text-red-600'
} else if (status.includes('存续') || status.includes('在营')) {
return 'bg-green-50 text-green-600'
} else if (status.includes('筹建') || status.includes('新设')) {
return 'bg-blue-50 text-blue-600'
} else {
return 'bg-yellow-50 text-yellow-600'
}
}
// 格式化日期显示
const formatDate = dateStr => {
if (!dateStr) return '—'
return dateStr
}
// 格式化资本金额显示
const formatCapital = (capital, currency) => {
if (!capital) return '—'
// 检查是否包含"万"字或需要显示为万元
let unit = ''
let value = parseFloat(capital)
// 处理原始数据中可能带有的单位
if (typeof capital === 'string' && capital.includes('万')) {
unit = '万'
// 提取数字部分
const numMatch = capital.match(/[\d.]+/)
value = numMatch ? parseFloat(numMatch[0]) : 0
} else if (value >= 10000) {
// 大额数字转换为万元显示
value = value / 10000
unit = '万'
}
// 格式化数字,保留两位小数(如果有小数部分)
const formattedValue = value.toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})
return `${formattedValue}${unit} ${currency || '人民币'}`
}
// 格式化占比
const formatRate = rate => {
if (!rate) return '—'
// 移除末尾不必要的0和小数点
const formatted = parseFloat(rate)
.toFixed(2)
.replace(/\.?0+$/, '')
return `${formatted}%`
}
// 格式化出资金额(特别处理,统一使用万元单位)
const formatSubscriptAmt = amount => {
if (!amount) return '—'
let value = parseFloat(amount)
// 处理原始数据中可能带有的单位
if (typeof amount === 'string') {
// 提取数字部分
const numMatch = amount.match(/[\d.]+/)
value = numMatch ? parseFloat(numMatch[0]) : 0
}
// 如果数值大于等于10000则转换为万元
if (value >= 10000) {
value = value / 10000
}
// 格式化数字,保留两位小数(如果有小数部分)
const formattedValue = value.toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})
return `${formattedValue} 万元`
}
// 获取持股比例对应的颜色样式
const getInvestRateClass = rate => {
if (!rate) return 'bg-gray-200'
const value = parseFloat(rate)
if (value >= 50) return 'bg-blue-500' // 控股或全资
if (value >= 20) return 'bg-blue-400' // 重要股东
if (value >= 10) return 'bg-blue-300' // 主要股东
return 'bg-blue-200' // 一般股东
}
// 获取持股比例的描述
const getInvestRateDesc = rate => {
if (!rate) return ''
const value = parseFloat(rate)
if (value >= 50) return '控股股东'
if (value >= 20) return '重要股东'
if (value >= 10) return '主要股东'
if (value > 0) return '一般股东'
return ''
}
</script>
<style lang="scss" scoped></style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,461 @@
<template>
<!-- 高管任职记录 -->
<div class="mb-8 card shadow-sm rounded-xl overflow-hidden">
<LTitle title="高管任职记录" type="blue-green" class="mb-4" />
<div v-if="managerPositions.length > 0" class="space-y-3">
<div v-for="(company, index) in managerPositions" :key="index" class="company-wrapper">
<!-- 企业卡片 - 可点击展开 -->
<div
class="bg-white rounded-lg shadow-sm border overflow-hidden border-gray-200 hover:border-purple-300 hover:shadow-md transition-all duration-300 cursor-pointer relative"
:class="{
'border-purple-400 shadow-md': isCompanyExpanded(company.orgName, 'manager', index),
}"
@click="toggleCompanyExpand(company.orgName, 'manager', index)"
>
<!-- 企业名称 - 紫色渐变背景 -->
<div class="bg-gradient-to-r from-purple-50 to-purple-100 px-4 py-2 flex items-center">
<div class="font-medium text-gray-800 truncate">{{ company.orgName }}</div>
</div>
<!-- 关系标签 - 靠左排列无背景 -->
<div class="px-4 py-1.5 flex items-center">
<div class="flex flex-wrap gap-1">
<span
v-for="(relation, idx) in company.relationship"
:key="idx"
class="px-1.5 py-0.5 text-xs rounded-full"
:class="getRelationshipClass(relation)"
>
{{ getRelationshipText(relation) }}
</span>
</div>
</div>
<!-- 企业状态注册资本注册时间 -->
<div
class="px-4 py-2 flex flex-col sm:flex-row sm:justify-between sm:items-center gap-2 text-xs text-gray-500"
>
<div class="flex items-center gap-3">
<span
v-if="company.basicInfo && company.basicInfo.regStatus"
class="px-1.5 py-0.5 rounded-full inline-block"
:class="getStatusClass(company.basicInfo.regStatus)"
>
{{ company.basicInfo.regStatus }}
</span>
<span v-if="company.basicInfo && company.basicInfo.regCapital">
{{ formatCapital(company.basicInfo.regCapital, company.basicInfo.regCapitalCurrency) }}
</span>
</div>
<div>
<span v-if="company.basicInfo && company.basicInfo.estiblishTime">
{{ formatDate(company.basicInfo.estiblishTime) }}
</span>
</div>
</div>
<!-- 展开指示器 - 放在右下角 -->
<div
class="absolute right-4 bottom-2 flex items-center text-xs text-gray-500 hover:text-purple-600 transition-colors duration-300"
:class="{
'text-purple-600': isCompanyExpanded(company.orgName, 'manager', index),
}"
>
<span class="mr-1">{{
isCompanyExpanded(company.orgName, 'manager', index) ? '收起详情' : '查看详情'
}}</span>
<div
class="w-5 h-5 rounded-full bg-gray-100 flex items-center justify-center transition-transform duration-300"
:class="{
'bg-purple-100 text-purple-600 rotate-180': isCompanyExpanded(company.orgName, 'manager', index),
}"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-3 w-3"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</div>
</div>
</div>
<!-- 企业详情抽屉 - 高管任职记录 -->
<div
class="overflow-hidden transition-all duration-500 ease-in-out"
:class="{
'max-h-0 opacity-0': !isCompanyExpanded(company.orgName, 'manager', index),
'max-h-[500px] opacity-100': isCompanyExpanded(company.orgName, 'manager', index),
}"
>
<div
class="mt-1 bg-gray-50 rounded-lg overflow-hidden p-4 pb-0 border border-gray-200 transform transition-all duration-300 max-h-[500px] overflow-y-auto"
>
<div class="relative">
<!-- 高管任职信息 -->
<div class="mb-6 relative">
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center mr-3">
{{ getManagerPositionStyle(company.relationship).icon }}
</div>
<div class="text-base font-medium text-purple-700">任职信息</div>
</div>
<div class="pl-10">
<div class="bg-purple-50 p-4 rounded-lg border border-purple-100">
<div class="grid grid-cols-1 gap-3">
<div>
<div class="text-xs text-gray-500 mb-1">担任职务</div>
<div class="flex items-center">
<span
class="text-sm font-medium"
:class="getManagerPositionStyle(company.relationship).textColor"
>
{{ getManagerPositionDesc(company.relationship) }}
</span>
<!-- <span class="ml-2 px-2 py-0.5 text-xs rounded-full"
:class="getManagerPositionStyle(company.relationship).bgColor + ' ' + getManagerPositionStyle(company.relationship).textColor">
{{ company.relationship.includes('tm') ? '现任' : '历史' }}
</span> -->
</div>
</div>
<div v-if="company.staffList && company.staffList.result && company.staffList.result.length > 0">
<div class="text-xs text-gray-500 mb-1">公司职位</div>
<div class="flex flex-wrap gap-1">
<template v-for="(staff, staffIdx) in company.staffList.result" :key="staffIdx">
<template v-if="staff.typeJoin && staff.typeJoin.length > 0">
<span
v-for="(position, posIdx) in staff.typeJoin"
:key="posIdx"
class="inline-block px-2 py-0.5 bg-purple-50 text-purple-600 text-xs rounded-full"
>
{{ position }}
</span>
</template>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 垂直连接线 -->
<div class="absolute left-0 top-8 bottom-0 w-0.5 bg-gray-200 ml-3"></div>
<!-- 分组信息展示 -->
<div v-if="company.basicInfo" class="space-y-6">
<div v-for="(group, groupIdx) in groupCompanyInfo(company)" :key="groupIdx" class="relative">
<!-- 分组标题 -->
<div class="flex items-center mb-3 relative z-10">
<div
class="w-7 h-7 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center mr-3"
>
{{ group.icon }}
</div>
<div class="text-base font-medium text-gray-700">
{{ group.title }}
</div>
</div>
<!-- 分组内容 -->
<div class="pl-10 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div
v-for="(field, fieldIdx) in group.fields"
:key="fieldIdx"
class="bg-white rounded-lg p-3 shadow-sm"
>
<div class="text-xs text-gray-500 mb-1">
{{ field.label }}
</div>
<div
v-if="field.type === 'status'"
class="text-sm px-2 py-0.5 rounded-full inline-block"
:class="getStatusClass(field.value)"
>
{{ field.value || '—' }}
</div>
<div v-else class="text-sm font-medium text-gray-800 flex items-center">
<span v-if="field.icon" class="mr-1">{{ field.icon }}</span>
{{ field.value || '' }}
</div>
</div>
</div>
</div>
</div>
<!-- 经营范围 -->
<div v-if="company.basicInfo && company.basicInfo.opscope" class="mt-6 relative">
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center mr-3">
📝
</div>
<div class="text-base font-medium text-gray-700">经营范围</div>
</div>
<div class="pl-10">
<div class="text-sm bg-white p-4 rounded-lg shadow-sm border border-gray-100 leading-relaxed">
{{ company.basicInfo.opscope }}
</div>
</div>
</div>
<!-- 主要人员 -->
<div
v-if="company.staffList && company.staffList.result && company.staffList.result.length > 0"
class="mt-6 relative"
>
<div class="flex items-center mb-3 relative z-10">
<div class="w-7 h-7 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center mr-3">
👥
</div>
<div class="text-base font-medium text-gray-700">主要人员</div>
</div>
<div class="pl-10">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
<div
v-for="(staff, staffIdx) in company.staffList.result"
:key="staffIdx"
class="flex items-center bg-white p-3 rounded-lg shadow-sm border border-gray-100 hover:border-purple-200 transition-all"
>
<div
class="w-10 h-10 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center text-white mr-3 text-sm font-medium"
>
{{ staff.name.substring(0, 1) }}
</div>
<div>
<div class="text-sm font-medium text-gray-800">
{{ staff.name }}
</div>
<div class="flex flex-wrap gap-1 mt-1">
<span
v-for="(position, posIdx) in staff.typeJoin || []"
:key="posIdx"
class="text-xs px-2 py-0.5 bg-purple-50 text-purple-600 rounded-full"
>
{{ position }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 底部收起按钮 -->
<div class="flex justify-center sticky bottom-0 pt-2 pb-2 bg-gradient-to-t from-gray-50 to-transparent">
<button
@click.stop="toggleCompanyExpand(company.orgName, 'manager', index)"
class="flex items-center px-4 py-2 rounded-full bg-purple-50 text-purple-600 hover:bg-purple-100 transition-colors duration-300"
>
<span class="mr-1">收起详情</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-4 w-4 transform rotate-180"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
</svg>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-else class="text-gray-500 py-10 text-center bg-gray-50 rounded-lg mx-4 mb-4">
<div class="text-gray-300 text-3xl mb-2">👔</div>
暂无高管任职记录
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
})
// 获取高管任职记录(高管、历史高管)
const managerPositions = computed(() => {
return props.data || []
})
onMounted(() => {
setTimeout(() => {
console.log('managerPositions', managerPositions.value)
}, 1000)
})
// 用于跟踪展开的企业卡片
const expandedCompanies = ref({})
// 切换展开/收起企业详情
const toggleCompanyExpand = (companyId, listType, index) => {
// 创建唯一标识符,包含企业名称、列表类型和索引
const uniqueKey = `${companyId}_${listType}_${index}`
expandedCompanies.value[uniqueKey] = !expandedCompanies.value[uniqueKey]
}
// 检查企业是否展开
const isCompanyExpanded = (companyId, listType, index) => {
const uniqueKey = `${companyId}_${listType}_${index}`
return !!expandedCompanies.value[uniqueKey]
}
// 企业信息分组函数
const groupCompanyInfo = company => {
if (!company || !company.basicInfo) return []
const basicInfo = company.basicInfo
return [
{
title: '基本信息',
icon: '🏢',
fields: [
{ label: '企业状态', value: basicInfo.regStatus, type: 'status' },
{ label: '统一社会信用代码', value: basicInfo.creditCode },
{ label: '注册号', value: basicInfo.regNumber },
{ label: '企业类型', value: basicInfo.entType },
{ label: '行业', value: basicInfo.industry },
{
label: '注册资本',
value: formatCapital(basicInfo.regCapital, basicInfo.regCapitalCurrency),
},
{ label: '成立日期', value: formatDate(basicInfo.estiblishTime), icon: '📅' },
{ label: '登记机关', value: basicInfo.regInstitute },
{ label: '核准日期', value: formatDate(basicInfo.approvedTime) },
],
},
{
title: '联系方式',
icon: '📞',
fields: [
{ label: '电话', value: basicInfo.phone },
{ label: '邮箱', value: basicInfo.email },
{ label: '网址', value: basicInfo.website },
{ label: '注册地址', value: basicInfo.regAddress },
],
},
{
title: '法人信息',
icon: '👨‍⚖️',
fields: [
{ label: '法人名称', value: basicInfo.legalPersonName },
{ label: '法人类型', value: basicInfo.legalPersonType },
],
},
]
}
// 关系映射表
const relationshipMap = {
sh: { text: '股东', color: 'bg-blue-100 text-blue-700', icon: '👑' },
his_sh: { text: '曾任股东', color: 'bg-blue-50 text-blue-600', icon: '🕒👑' },
lp: { text: '法人', color: 'bg-green-100 text-green-700', icon: '⚖️' },
his_lp: { text: '曾任法人', color: 'bg-green-50 text-green-600', icon: '🕒⚖️' },
tm: { text: '高管', color: 'bg-purple-100 text-purple-700', icon: '👔' },
his_tm: { text: '曾任高管', color: 'bg-purple-50 text-purple-600', icon: '🕒👔' },
}
// 获取关系文本
const getRelationshipText = relation => {
return relationshipMap[relation]?.text || relation
}
// 获取关系样式类
const getRelationshipClass = relation => {
return relationshipMap[relation]?.color || ''
}
// 高管类关系
const managerRelations = ['tm', 'his_tm']
// 获取企业状态对应的样式
const getStatusClass = status => {
if (!status) return 'bg-gray-100 text-gray-500'
if (status.includes('注销') || status.includes('吊销')) {
return 'bg-red-50 text-red-600'
} else if (status.includes('存续') || status.includes('在营')) {
return 'bg-green-50 text-green-600'
} else if (status.includes('筹建') || status.includes('新设')) {
return 'bg-blue-50 text-blue-600'
} else {
return 'bg-yellow-50 text-yellow-600'
}
}
// 格式化日期显示
const formatDate = dateStr => {
if (!dateStr) return '—'
return dateStr
}
// 格式化资本金额显示
const formatCapital = (capital, currency) => {
if (!capital) return '—'
// 检查是否包含"万"字或需要显示为万元
let unit = ''
let value = parseFloat(capital)
// 处理原始数据中可能带有的单位
if (typeof capital === 'string' && capital.includes('万')) {
unit = '万'
// 提取数字部分
const numMatch = capital.match(/[\d.]+/)
value = numMatch ? parseFloat(numMatch[0]) : 0
} else if (value >= 10000) {
// 大额数字转换为万元显示
value = value / 10000
unit = '万'
}
// 格式化数字,保留两位小数(如果有小数部分)
const formattedValue = value.toLocaleString('zh-CN', {
minimumFractionDigits: 0,
maximumFractionDigits: 2,
})
return `${formattedValue}${unit} ${currency || '人民币'}`
}
// 获取高管职位的描述
const getManagerPositionDesc = relations => {
if (!relations || relations.length === 0) return '—'
const positions = []
relations.forEach(relation => {
if (managerRelations.includes(relation)) {
positions.push(getRelationshipText(relation))
}
})
return positions.length > 0 ? positions.join('、') : '—'
}
// 获取高管职位对应的图标和颜色
const getManagerPositionStyle = relations => {
let isCurrent = false
if (relations && relations.length > 0) {
// 检查是否有当前高管职位
isCurrent = relations.some(r => r === 'tm')
}
return {
icon: isCurrent ? '👔' : '🕒',
color: isCurrent ? 'bg-purple-500' : 'bg-purple-300',
textColor: isCurrent ? 'text-purple-700' : 'text-purple-500',
bgColor: isCurrent ? 'bg-purple-50' : 'bg-purple-50/50',
}
}
</script>
<style lang="scss" scoped></style>

View File

@@ -13,7 +13,7 @@ const entCollapse = ref([]);
<template>
<div class="">
<template v-if="data && data.datalist.length === 0">
<template v-if="!data || data.datalist?.length === 0">
<!-- 显示暂无关联企业信息的提示 -->
<div class="card py-10 text-center text-gray-500">
暂无关联企业信息
@@ -438,7 +438,7 @@ const entCollapse = ref([]);
<span class="flex-shrink-0 text-gray-700 font-medium">
法人姓名:
</span>
<span class="text-gray-600">
<span class="text-gray-600">s
{{ penalty.legalPersonName }}
</span>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -1,267 +1,262 @@
<script setup>
import LTitle from "@/components/LTitle.vue";
import LTitle from '@/components/LTitle.vue'
import throttle from "lodash/throttle";
import throttle from 'lodash/throttle'
// 接收 props
const props = defineProps({
type: {
type: String,
default: "purple-pink", // 默认颜色渐变
},
tabs: {
type: Array,
required: true, // tabs 的选项数组
},
});
type: {
type: String,
default: 'purple-pink', // 默认颜色渐变
},
tabs: {
type: Array,
required: true, // tabs 的选项数组
},
})
// 当前选中的 tab
const selected = ref(props.tabs[0].value);
const showPopup = ref(false); // 控制弹出层的显示状态
const onSelect = ref(false);
const selected = ref(props.tabs[0].value)
const showPopup = ref(false) // 控制弹出层的显示状态
const onSelect = ref(false)
// 根据 type 动态生成分割线的类名
const lineClass = computed(() => {
switch (props.type) {
case "blue-green":
return "bg-gradient-to-r from-blue-400 via-green-500 to-teal-500";
case "orange-yellow":
return "bg-gradient-to-r from-orange-400 via-yellow-500 to-yellow-600";
case "red-purple":
return "bg-gradient-to-r from-red-500 via-purple-500 to-purple-600";
default:
return "bg-gradient-to-r from-purple-400 via-pink-500 to-red-500";
}
});
const tabWidths = ref([]);
switch (props.type) {
case 'blue-green':
return 'bg-gradient-to-r from-blue-400 via-green-500 to-teal-500'
case 'orange-yellow':
return 'bg-gradient-to-r from-orange-400 via-yellow-500 to-yellow-600'
case 'red-purple':
return 'bg-gradient-to-r from-red-500 via-purple-500 to-purple-600'
case 'sky-blue':
return 'bg-gradient-to-r from-sky-500 via-blue-500 to-blue-600'
default:
return 'bg-gradient-to-r from-purple-400 via-pink-500 to-red-500'
}
})
const tabWidths = ref([])
// 计算滑动线的位置和宽度
const slideLineStyle = computed(() => {
const index = props.tabs.findIndex((tab) => tab.value === selected.value);
const width = tabWidths.value[index] || 0;
const offsetLeft = tabWidths.value
.slice(0, index)
.reduce((acc, w) => acc + w, 0);
return {
width: `${width}px`,
transform: `translateX(${offsetLeft}px)`,
};
});
const index = props.tabs.findIndex(tab => tab.value === selected.value)
const width = tabWidths.value[index] || 0
const offsetLeft = tabWidths.value.slice(0, index).reduce((acc, w) => acc + w, 0)
return {
width: `${width}px`,
transform: `translateX(${offsetLeft}px)`,
}
})
// 获取每个 tab 的宽度
function updateTabWidths() {
const query = uni.createSelectorQuery();
const query = uni.createSelectorQuery()
props.tabs.forEach((tab) => {
query.select(`#tab-${tab.value}`).boundingClientRect();
});
props.tabs.forEach(tab => {
query.select(`#tab-${tab.value}`).boundingClientRect()
})
query.exec((res) => {
tabWidths.value = res.map((rect) => (rect ? rect.width : 0));
});
query.exec(res => {
tabWidths.value = res.map(rect => (rect ? rect.width : 0))
})
}
function onSelectChange() {
onSelect.value = true;
setTimeout(() => {
onSelect.value = false;
}, 300);
onSelect.value = true
setTimeout(() => {
onSelect.value = false
}, 300)
}
// 获取视口滚动位置的异步函数
function getViewportScrollOffset() {
return new Promise((resolve) => {
const query = uni.createSelectorQuery();
query
.selectViewport()
.scrollOffset((viewport) => {
resolve(viewport);
})
.exec();
});
return new Promise(resolve => {
const query = uni.createSelectorQuery()
query
.selectViewport()
.scrollOffset(viewport => {
resolve(viewport)
})
.exec()
})
}
// 获取元素相对视口的位置信息的异步函数
function getElementRect(selector) {
return new Promise((resolve) => {
const query = uni.createSelectorQuery();
query
.select(selector)
.boundingClientRect((rect) => {
resolve(rect);
})
.exec();
});
return new Promise(resolve => {
const query = uni.createSelectorQuery()
query
.select(selector)
.boundingClientRect(rect => {
resolve(rect)
})
.exec()
})
}
// 主函数:按顺序执行两个查询
async function selectTab(tab) {
console.log("one selectTab");
selected.value = tab.value;
updateTabWidths();
onSelectChange();
console.log('one selectTab')
selected.value = tab.value
updateTabWidths()
onSelectChange()
const offset = 100; // 自定义偏移量
const offset = 100 // 自定义偏移量
try {
// 第一步:获取视口的滚动位置
const viewport = await getViewportScrollOffset();
console.log("viewport", viewport);
try {
// 第一步:获取视口的滚动位置
const viewport = await getViewportScrollOffset()
console.log('viewport', viewport)
// 第二步:获取目标元素的布局信息
const rect = await getElementRect(`#${tab.value}`);
console.log("rect", rect);
// 第二步:获取目标元素的布局信息
const rect = await getElementRect(`#${tab.value}`)
console.log('rect', rect)
const systemInfo = uni.getSystemInfoSync();
let scrollTop = viewport.scrollTop + rect.top - offset;
const systemInfo = uni.getSystemInfoSync()
let scrollTop = viewport.scrollTop + rect.top - offset
if (process.env.UNI_PLATFORM === "mp-weixin") {
scrollTop += systemInfo.statusBarHeight || 20; // 微信小程序增加状态栏高度
} else if (process.env.UNI_PLATFORM === "app-plus") {
scrollTop += systemInfo.statusBarHeight || 20; // App 端增加状态栏高度
}
console.log("目标滚动位置 scrollTop:", scrollTop);
// 执行滚动到目标位置
uni.pageScrollTo({
scrollTop,
duration: 300,
});
} catch (error) {
console.error("滚动过程中出现错误:", error);
if (process.env.UNI_PLATFORM === 'mp-weixin') {
scrollTop += systemInfo.statusBarHeight || 20 // 微信小程序增加状态栏高度
} else if (process.env.UNI_PLATFORM === 'app-plus') {
scrollTop += systemInfo.statusBarHeight || 20 // App 端增加状态栏高度
}
console.log('目标滚动位置 scrollTop:', scrollTop)
// 执行滚动到目标位置
uni.pageScrollTo({
scrollTop,
duration: 300,
})
} catch (error) {
console.error('滚动过程中出现错误:', error)
}
}
// 控制右侧图标按钮点击弹出所有选项的 Popup
function openPopup() {
showPopup.value = true;
showPopup.value = true
}
function selectFromPopup(tab) {
selectTab(tab);
showPopup.value = false;
selectTab(tab)
showPopup.value = false
}
const windowHeight = uni.getSystemInfoSync().windowHeight / 2; // 缓存窗口高度
const windowHeight = uni.getSystemInfoSync().windowHeight / 2 // 缓存窗口高度
function onScroll() {
if (onSelect.value) return;
if (onSelect.value) return
const offset = 0;
const query = uni.createSelectorQuery();
const offset = 0
const query = uni.createSelectorQuery()
// 为每个 tab 设置查询请求
props.tabs.forEach((tab) => {
query.select(`#${tab.value}`).boundingClientRect();
});
// 为每个 tab 设置查询请求
props.tabs.forEach(tab => {
query.select(`#${tab.value}`).boundingClientRect()
})
query.exec((res) => {
for (let index = 0; index < res.length; index++) {
const rect = res[index];
if (rect && rect.top >= offset && rect.top < windowHeight) {
if (selected.value !== props.tabs[index].value) {
selected.value = props.tabs[index].value;
updateTabWidths();
}
break; // 找到匹配的 tab 后立即退出循环
}
query.exec(res => {
for (let index = 0; index < res.length; index++) {
const rect = res[index]
if (rect && rect.top >= offset && rect.top < windowHeight) {
if (selected.value !== props.tabs[index].value) {
selected.value = props.tabs[index].value
updateTabWidths()
}
});
break // 找到匹配的 tab 后立即退出循环
}
}
})
}
// 使用节流来减少 onScroll 调用频率
const throttledOnScroll = throttle(onScroll, 100); // 每 100ms 触发一次
const throttledOnScroll = throttle(onScroll, 100) // 每 100ms 触发一次
onMounted(() => {
updateTabWidths();
updateTabWidths()
// #ifdef H5
window.addEventListener("scroll", onScroll);
// #endif
});
// #ifdef H5
window.addEventListener('scroll', onScroll)
// #endif
})
onUnmounted(() => {
// #ifdef H5
window.removeEventListener("scroll", onScroll);
// #endif
});
// #ifdef H5
window.removeEventListener('scroll', onScroll)
// #endif
})
// #ifdef MP-WEIXIN || APP-PLUS
onPageScroll(() => {
throttledOnScroll();
});
throttledOnScroll()
})
// #endif
const scrollTab = computed(() => {
let index = 0;
let selectedTab = "";
for (const i in props.tabs) {
if (props.tabs[i].value === selected.value) {
index = Number.parseInt(i);
break;
}
let index = 0
let selectedTab = ''
for (const i in props.tabs) {
if (props.tabs[i].value === selected.value) {
index = Number.parseInt(i)
break
}
if (index === 1) {
selectedTab = props.tabs[index - 1].value;
} else if (index < 2 || index === props.tabs.length) {
selectedTab = selected.value;
} else {
selectedTab = props.tabs[index - 1].value;
}
return `tab-${selectedTab}`;
});
}
if (index === 1) {
selectedTab = props.tabs[index - 1].value
} else if (index < 2 || index === props.tabs.length) {
selectedTab = selected.value
} else {
selectedTab = props.tabs[index - 1].value
}
return `tab-${selectedTab}`
})
</script>
<template>
<div class="fixed z-1000 bg-white px-8">
<scroll-view
:scroll-into-view="scrollTab"
scroll-x
:show-scrollbar="false"
>
<div class="relative flex">
<div
v-for="(tab, index) in tabs"
:id="`tab-${tab.value}`"
:key="index"
class="h-8 min-w-1/5 shrink-0 cursor-pointer py-1 text-center text-xs font-bold line-height-6 line-height-8 transition-transform duration-200 ease-in-out"
:class="{
'text-gray-900 ': selected === tab.value,
'text-gray-500': selected !== tab.value,
}"
@click="selectTab(tab)"
>
{{ tab.label }}
</div>
<div
class="absolute bottom-0 h-1 rounded transition-all duration-300"
:style="slideLineStyle"
:class="lineClass"
/>
</div>
</scroll-view>
<div class="fixed z-1000 bg-white px-8">
<scroll-view :scroll-into-view="scrollTab" scroll-x :show-scrollbar="false">
<div class="relative flex">
<div
class="absolute right-0 top-0 h-6 w-8 cursor-pointer bg-white py-2 text-center line-height-6"
@click="openPopup"
v-for="(tab, index) in tabs"
:id="`tab-${tab.value}`"
:key="index"
class="h-8 min-w-1/5 shrink-0 cursor-pointer py-1 text-center text-xs font-bold line-height-6 line-height-8 transition-transform duration-200 ease-in-out"
:class="{
'text-gray-900 ': selected === tab.value,
'text-gray-500': selected !== tab.value,
}"
@click="selectTab(tab)"
>
<wd-icon name="view-list" size="22px" />
{{ tab.label }}
</div>
<WdPopup v-model="showPopup" position="right" class="w-3/4">
<view class="pt-20">
<view
v-for="tab in tabs"
:key="tab.value"
class="p-2 text-xs font-bold"
:class="{ 'bg-gray-200 ': selected === tab.value }"
@click="selectFromPopup(tab)"
>
{{ tab.label }}
</view>
</view>
</WdPopup>
<div
class="absolute bottom-0 h-1 rounded transition-all duration-300"
:style="slideLineStyle"
:class="lineClass"
/>
</div>
</scroll-view>
<div
class="absolute right-0 top-0 h-6 w-8 cursor-pointer bg-white py-2 text-center line-height-6"
@click="openPopup"
>
<wd-icon name="view-list" size="22px" />
</div>
<WdPopup v-model="showPopup" position="right" class="w-3/4">
<div class="pt-20">
<div
v-for="tab in tabs"
:key="tab.value"
class="p-2 text-xs font-bold"
:class="{ 'bg-gray-200 ': selected === tab.value }"
@click="selectFromPopup(tab)"
>
{{ tab.label }}
</div>
</div>
</WdPopup>
</div>
</template>
<style scoped>
.iconfont {
font-size: 20px;
font-size: 20px;
}
</style>

View File

@@ -2,9 +2,12 @@
<!-- 授权书滚动区域 -->
<div class=" card flex-1 overflow-y-auto" ref="agreementBox" @scroll="handleScroll">
<p class="my-2">广西福铭网络科技有限公司</p>
<p class="text-center text-lg font-bold text-red-500 my-2">
(未成年人不得使用)
</p>
<p class="indent-[2em]">
<!-- <span class="font-bold"> {{ signature ? userData.name : "____________" }}</span> -->
本人拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
本人拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告及查询可能与我本人有关的公开的法律信息因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
</p>
<p class="mt-2 font-bold">授权内容如下</p>
<ol class="list-decimal pl-6">
@@ -14,10 +17,16 @@
<li>
依法成立的第三方服务商查询或核实搜集保存处理共享使用含合法业务应用本人相关数据且不再另行告知本人但法律法规监管政策禁止的除外
</li>
<li>
本人授权贵司通过相关渠道查询可能与我本人有关的公开的法律信息用于协助我了解自身法律状况
</li>
<!-- <li>本人授权本业务推广方 可浏览本人大数据报告</li> -->
<li>
本人授权有效期为自授权之日起
1个月本授权为不可撤销授权但法律法规另有规定的除外
1个月本授权为不可撤销授权但法律法规另有规定的除外平台在我的授权下不构成任何对我本人的侵权行为
</li>
<li>
我点击本项打V则视为我本人同意授权本次查询
</li>
</ol>
<p class="mt-2 font-bold">用户声明与承诺</p>
@@ -31,6 +40,9 @@
<li>
若用户冒名签署或提供虚假信息由用户自行承担全部法律责任平台不承担任何后果
</li>
<li>
我声明我是完全民事行为能力人我完全理解并同意上述所有内容授权是我真实意思表示
</li>
</ul>
<p class="mt-2 font-bold">特别提示</p>
<ul class="list-decimal pl-6">
@@ -49,6 +61,12 @@
<li>
本授权书中涉及的数据查询和报告生成由依法成立的第三方服务商提供若因第三方行为导致数据错误或损失用户应向第三方主张权利平台不承担相关责任
</li>
<li>
已公开的司法数据未必有您本人(或者委托人)的信息请知悉!
</li>
<li>
本服务是虚拟数据服务一经提交查询则会产生后台信息服务
</li>
</ul>
<p class="mt-2 font-bold">附加说明</p>
<ul class="list-decimal pl-6">
@@ -59,7 +77,7 @@
本人有权随时撤回本授权书中的授权但撤回前的授权行为及其法律后果仍具有法律效力若需撤回授权本人可通过贵司官方渠道提交书面申请贵司将在收到申请后依法停止对本人数据的使用
</li>
<li>
你通过天远查自愿支付相应费用用于购买广西福铭网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
你通过"天远查"自愿支付相应费用用于购买广西福铭网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP"联系客服"按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
</li>
<li>
你向广西福铭网络科技有限公司的支付方式为广西福铭网络科技有限公司及其经官方授权的相关企业的支付宝账户
@@ -68,7 +86,7 @@
<p class="mt-2 font-bold">争议解决机制</p>
<ul>
<li>
若因本授权书引发争议双方应友好协商解决协商不成的双方同意将争议提交至授权书签署地海南省有管辖权的人民法院解决
若因本授权书引发争议双方应友好协商解决协商不成的双方同意将争议提交至授权书签署地广西壮族自治区有管辖权的人民法院解决
</li>
</ul>
<p class="mt-2 font-bold">签署方式的法律效力声明</p>

View File

@@ -1,261 +0,0 @@
<script setup>
import CBad from "@/ui/CBad.vue";
import CBankLoanApplication from "@/ui/CBankLoanApplication.vue";
import CBankLoanBehavior from "@/ui/CBankLoanBehavior.vue";
import CLawsuit from "@/ui/CLawsuit.vue";
import CRelatedEnterprises from "@/ui/CRelatedEnterprises.vue";
import CSpecialList from "@/ui/CSpecialList.vue";
import { useHttp } from "@/composables/useHttp";
// import CTabs from "@/ui/CTabs.vue";
// import { queryResultByOrder } from "@/api/apis";
import CMarriage from "@/ui/CMarriage.vue";
import { useFetch } from "@vueuse/core";
const productMap = {
1: "背景调查",
2: "企业报告",
3: "家政服务",
4: "婚姻状态",
5: "贷前背景调查",
6: "租赁服务",
7: "个人风险评估",
};
// 根据 product_id 获取产品名称
function getProductName(productId) {
return productMap[productId] || "未知类型";
}
const productId = ref(null);
const isDone = ref(true);
const entData = ref(null);
const lawsuitData = ref(null);
const badData = ref(null);
const specialData = ref(null);
const bankLoanApplicationData = ref(null);
const marriageData = ref(null);
const bankLoanBehavior = ref(null);
const feature = ref(null);
const token = ref(null);
const tabs = ref([{ label: "报告概述", value: "overdiv" }]);
const reportItems = ref([]);
const sortedReportItems = computed(() => {
return reportItems.value.slice().sort((a, b) => a.sort - b.sort);
});
const sortedTabs = computed(() => {
return tabs.value.slice().sort((a, b) => a.sort - b.sort);
});
onMounted(() => {
const query = new URLSearchParams(window.location.search);
feature.value = query.get("feature");
token.value = query.get("token") || "";
if (!feature.value) return;
const { data, isFetching, error, onFetchResponse } = useFetch(
`/api/v1/query/example?feature=${feature.value}`,
{
async beforeFetch({ url, options, cancel }) {
options.headers = {
...options.headers,
Authorization: token.value,
};
return {
options,
};
},
}
)
.get()
.json();
onFetchResponse(() => {
console.log("data", data.value);
if (data.value.code === 200) {
productId.value = data.value.data.product_id;
data.value.data.query_data.forEach((item) => {
if (item.success) {
switch (item.apiID) {
case "G09SC02":
marriageData.value = item.data;
tabs.value.push({
label: "婚姻状态",
value: "marriage",
sort: 1,
});
reportItems.value.push({
label: "婚姻状态",
value: "marriage",
sort: 1,
});
break;
case "G27BJ05":
bankLoanApplicationData.value = item.data;
tabs.value.push({
label: "借贷申请记录",
value: "netloan",
sort: 7,
});
reportItems.value.push({
label: "借贷申请记录",
value: "netloan",
sort: 7,
});
break;
case "G28BJ05":
bankLoanBehavior.value = item.data;
tabs.value.push({
label: "借贷记录",
value: "loan",
sort: 6,
});
reportItems.value.push({
label: "借贷记录",
value: "loan",
sort: 6,
});
break;
case "G26BJ05":
specialData.value = item.data;
tabs.value.push({
label: "异常名单",
value: "special",
sort: 5,
});
reportItems.value.push({
label: "异常名单",
value: "special",
sort: 5,
});
break;
case "G05HZ01":
entData.value = item.data;
tabs.value.push({
label: "关联企业",
value: "ent",
sort: 4,
});
reportItems.value.push({
label: "关联企业",
value: "ent",
sort: 4,
});
break;
case "G34BJ03":
badData.value = item.data;
tabs.value.push({
label: "不良风险评估",
value: "bad",
sort: 3,
});
reportItems.value.push({
label: "不良风险评估",
value: "bad",
sort: 3,
});
break;
case "G35SC01":
lawsuitData.value = item.data;
tabs.value.push({
label: "涉诉案件",
value: "lawsuit",
sort: 2,
});
reportItems.value.push({
label: "涉诉案件",
value: "lawsuit",
sort: 2,
});
break;
default:
console.log(`未知的apiID: ${item.apiID}`);
}
}
});
}
});
});
</script>
<template>
<div class="min-h-full from-blue-100 to-white bg-gradient-to-b">
<template v-if="isDone">
<div class="flex flex-col gap-y-4 p-4 pt-12">
<div id="overdiv" class="title">报告概述</div>
<div class="card">
<div class="flex flex-col gap-y-2">
<div class="flex justify-between">
<span class="text-gray-700 font-bold">报告时间</span>
<span class="text-gray-600">2024年11月18日 23:11:23</span>
</div>
<div class="flex justify-between">
<span class="text-gray-700 font-bold">报告项目</span>
<span class="text-gray-600">{{
getProductName(productId)
}}</span>
</div>
</div>
<div>
<LTitle class="my-4" title="报告内容" type="blue-green" />
<div class="flex flex-col gap-y-2">
<div v-for="item in sortedReportItems" :key="item.value" class="flex justify-between">
<span class="text-gray-700 font-bold">{{ item.label }}</span>
<span class="text-green-500 font-bold">已解锁</span>
</div>
</div>
</div>
</div>
<template v-if="marriageData">
<div id="marriage" class="title">婚姻状态</div>
<CMarriage :data="marriageData" />
</template>
<template v-if="lawsuitData">
<div id="lawsuit" class="title">涉诉案件</div>
<CLawsuit :data="lawsuitData" />
</template>
<template v-if="badData">
<div id="bad" class="title">不良风险评估</div>
<CBad :data="badData" />
</template>
<template v-if="entData">
<div id="ent" class="title">关联企业</div>
<CRelatedEnterprises :data="entData" />
</template>
<template v-if="specialData">
<div id="special" class="title">异常名单</div>
<CSpecialList :data="specialData" />
</template>
<template v-if="bankLoanBehavior">
<div id="loan" class="title">借贷记录</div>
<CBankLoanBehavior :data="bankLoanBehavior" />
</template>
<template v-if="bankLoanApplicationData">
<div id="netloan" class="title">贷款申请记录</div>
<CBankLoanApplication :data="bankLoanApplicationData" />
</template>
<div class="card">
<div>
<div class="text-bold text-blue-500 mb-2">报告说明</div>
<div>
本报告的数据由用户本人明确授权后我们才向相关合法存有用户个人数据的机构调取本报告相关内容本平台只做大数据的获取与分析仅向用户个人展示参考
</div>
<p>
&nbsp; &nbsp; 报告有效期<strong class="text-red-500">30</strong>过期自动删除
</p>
<p>
&nbsp; &nbsp;
若您的数据不全面可能是数据具有延迟性或者合作信息机构未获取到您的数据若数据有错误请联系客服
</p>
</div>
</div>
</div>
</template>
</div>
</template>
<style lang="scss" scoped>
.title {
@apply mx-auto mt-2 w-64 border rounded-3xl bg-gradient-to-r from-blue-400 via-green-500 to-teal-500 py-2 text-center text-white font-bold;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -1,99 +1,116 @@
<script setup>
import { ref, onMounted } from 'vue'
const router = useRouter()
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const reportList = ref([])
const num = ref(0)
const max = ref(60)
const loading = ref(false)
const finished = ref(false)
import { ref, onMounted } from "vue";
const router = useRouter();
const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
const reportList = ref([]);
const num = ref(0);
const max = ref(60);
const loading = ref(false);
const finished = ref(false);
// 初始加载数据
async function fetchData() {
// { page: page.value, page_size: pageSize.value }
loading.value = true
const { data, error } = await useApiFetch(`query/list?page=${page.value}&page_size=${pageSize.value}`)
loading.value = true;
const { data, error } = await useApiFetch(
`query/list?page=${page.value}&page_size=${pageSize.value}`
)
.get()
.json()
.json();
if (data.value && !error.value) {
if (data.value.code === 200) {
total.value = data.value.data.total
total.value = data.value.data.total;
if (data.value.data.list && data.value.data.list.length > 0) {
reportList.value.push(...data.value.data.list)
page.value += 1
reportList.value.push(...data.value.data.list);
page.value += 1;
}
if (reportList.value.length >= total.value) {
finished.value = true
finished.value = true;
}
}
}
loading.value = false
loading.value = false;
}
// 初始加载
onMounted(() => {
fetchData()
})
fetchData();
});
// 下拉触底加载更多
const onLoad = () => {
if (!finished.value) {
console.log("finished", finished.value)
console.log("finished", finished.value);
if (num.value >= max.value) {
finished.value = true
finished.value = true;
} else {
fetchData()
fetchData();
}
}
}
};
function toDetail(item) {
router.push({ path: '/report', query: { orderId: item.order_id } });
router.push({ path: "/report", query: { orderId: item.order_id } });
}
// 状态文字映射
function stateText(state) {
switch (state) {
case 'pending':
return '查询中'
case 'success':
return '查询成功'
case 'failed':
return '查询失败'
case "pending":
return "查询中";
case "success":
return "查询成功";
case "failed":
return "查询失败";
default:
return '未知状态'
return "未知状态";
}
}
// 状态颜色映射
function statusClass(state) {
switch (state) {
case 'pending':
return 'status-pending'
case 'success':
return 'status-success'
case 'failed':
return 'status-failed'
case "pending":
return "status-pending";
case "success":
return "status-success";
case "failed":
return "status-failed";
default:
return ''
return "";
}
}
</script>
<template>
<!-- Vant 通知栏 -->
<van-notice-bar color="#1989fa" background="#ecf9ff" left-icon="info-o"
text="为保证用户的隐私以及数据安全您的报告生成30天之后将自动清除请及时保存您的报告。" />
<van-notice-bar
color="#1989fa"
background="#ecf9ff"
left-icon="info-o"
text="为保证用户的隐私以及数据安全您的报告生成15天之后将自动清除请及时保存您的报告。"
/>
<div class="flex flex-col gap-4 p-4">
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"><van-cell
v-for="item in reportList" :key="item.id" @click="toDetail(item)" class="card mb-4">
<div class=" text-gray-600 flex flex-col gap-2">
<van-list
v-model:loading="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
><van-cell
v-for="item in reportList"
:key="item.id"
@click="toDetail(item)"
class="card mb-4"
>
<div class="text-gray-600 flex flex-col gap-2">
<div class="flex items-center justify-between">
<div>状态:</div>
<div class="rounded-xl px-2 py-1" :class="[statusClass(item.query_state)]">
<div
class="rounded-xl px-2 py-1"
:class="[statusClass(item.query_state)]"
>
{{ stateText(item.query_state) }}
</div>
</div>
@@ -108,7 +125,6 @@ function statusClass(state) {
</div>
</van-cell>
</van-list>
</div>
</template>

View File

@@ -8,7 +8,7 @@ import Payment from "@/components/Payment.vue";
import CarNumberInput from "@/components/CarNumberInput.vue";
const route = useRoute();
const router = useRouter()
const router = useRouter();
const showAuthorizationPopup = ref(false);
const authorization = ref(true);
const showPayment = ref(false);
@@ -22,9 +22,9 @@ const idCardWoman = ref("");
const mobile = ref("");
const bankCard = ref("");
const certificateNumber = ref("");
const startDate = ref([])
const dateVal = ref("")
const showDatePicker = ref(false)
const startDate = ref([]);
const dateVal = ref("");
const showDatePicker = ref(false);
// 新增车架号和行驶证照片
const vinCode = ref("");
const carDrivingPermit = ref("");
@@ -33,11 +33,13 @@ const carDrivingPermitFile = ref(null);
const today = new Date();
const maxDate = today; // 最大日期为当前日期
// 最小日期为2000年1月1日
const minDate = new Date('2000-01-01');
const minDate = new Date("2000-01-01");
const entName = ref("");
const entCode = ref("");
const verificationCode = ref("");
const agreeToTerms = ref(false);
// 新增特殊API的授权书勾选
const agreeToAuthorization = ref(false);
const isCountingDown = ref(false);
const countdown = ref(60);
const feature = ref(route.params.feature);
@@ -73,23 +75,23 @@ const carTypeColumns = [
{ value: "52", text: "新能源小型车" },
];
const formatterDate = (type, option) => {
if (type === 'year') {
option.text += '年';
if (type === "year") {
option.text += "年";
}
if (type === 'month') {
option.text += '月';
if (type === "month") {
option.text += "月";
}
if (type === 'day') {
option.text += '日';
if (type === "day") {
option.text += "日";
}
return option;
};
const onConfirmDate = ({ selectedValues, selectedOptions }) => {
console.log("selectedValues", selectedValues)
console.log("startDate", startDate.value)
dateVal.value = selectedOptions.map(item => item.text).join('');
showDatePicker.value = false
}
console.log("selectedValues", selectedValues);
console.log("startDate", startDate.value);
dateVal.value = selectedOptions.map((item) => item.text).join("");
showDatePicker.value = false;
};
const carLicenseChange = (e) => {
console.log("carLicenseChange", e);
carLicense.value = e;
@@ -109,7 +111,7 @@ const onConfirmCarType = ({ selectedValues, selectedOptions }) => {
const fraudGamblingCheckType = ref("mobile"); // 默认选择手机号
onMounted(() => {
isFinishPayment()
isFinishPayment();
getProduct();
initAuthorization();
});
@@ -117,7 +119,7 @@ function isFinishPayment() {
const query = new URLSearchParams(window.location.search);
let orderNo = query.get("out_trade_no");
if (orderNo) {
router.push({ path: '/report', query: { orderNo } });
router.push({ path: "/report", query: { orderNo } });
}
}
async function getProduct() {
@@ -127,8 +129,17 @@ async function getProduct() {
if (data.value) {
featureData.value = data.value.data;
// 在获取产品数据后设置授权勾选状态
setAuthorizationCheckboxes();
}
}
// 设置授权勾选状态
function setAuthorizationCheckboxes() {
// 移除默认勾选逻辑,让用户必须手动勾选
// 仅保留函数作为以后可能需要的扩展点
}
function initAuthorization() {
if (NeedAuthorization.includes(feature.value)) {
authorization.value = false;
@@ -179,14 +190,14 @@ const compressAndConvertToBase64 = (file) => {
// 如果文件小于1MB直接处理不压缩
if (file.size <= MAX_SIZE) {
// 去除base64前缀
const base64String = event.target.result.split(',')[1];
const base64String = event.target.result.split(",")[1];
resolve(base64String);
return;
}
// 需要压缩
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// 计算压缩比例
let ratio = 1;
@@ -203,7 +214,9 @@ const compressAndConvertToBase64 = (file) => {
ctx.drawImage(img, 0, 0, width, height);
// 尝试压缩为JPEG格式效果更好
const compressedBase64 = canvas.toDataURL('image/jpeg', 0.8).split(',')[1];
const compressedBase64 = canvas
.toDataURL("image/jpeg", 0.8)
.split(",")[1];
// 如果还是太大,继续压缩
if (compressedBase64.length * 0.75 > MAX_SIZE) {
@@ -212,7 +225,9 @@ const compressAndConvertToBase64 = (file) => {
canvas.width = img.width * smallerRatio;
canvas.height = img.height * smallerRatio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const moreCompressedBase64 = canvas.toDataURL('image/jpeg', 0.7).split(',')[1];
const moreCompressedBase64 = canvas
.toDataURL("image/jpeg", 0.7)
.split(",")[1];
resolve(moreCompressedBase64);
} else {
resolve(compressedBase64);
@@ -234,7 +249,7 @@ const handleCarDrivingPermitUpload = async (event) => {
if (!file) return;
// 检查是否是图片
if (!file.type.match('image.*')) {
if (!file.type.match("image.*")) {
showToast({ message: "请上传图片文件" });
return;
}
@@ -245,7 +260,7 @@ const handleCarDrivingPermitUpload = async (event) => {
carDrivingPermit.value = base64String;
showToast({ message: "行驶证上传成功", type: "success" });
} catch (error) {
console.error('图片处理失败', error);
console.error("图片处理失败", error);
showToast({ message: "图片处理失败,请重试" });
}
};
@@ -277,21 +292,72 @@ const isBankCardValid = computed(() => {
return sum % 10 === 0; // 如果最终和能被 10 整除,则银行卡号有效
});
// 判断是否包含特殊API ID需要单独勾选授权书
const needSeparateAuthorization = computed(() => {
const specialApiIds = [
"G22SC01",
"Q03SC01",
"G37SC01",
"G36SC01",
"G05HZ01",
];
if (!featureData.value.features || !featureData.value.features.length) {
return false;
}
return featureData.value.features.some((feature) =>
specialApiIds.includes(feature.api_id)
);
});
// 修改验证函数来检查授权书的勾选状态
function handleSubmit() {
// 基本协议验证
if (!agreeToTerms.value) {
showToast({ message: `请阅读并同意用户协议、隐私政策${!NeedAuthorization.includes(feature.value) ? '和授权书' : ''}` });
// 根据不同情况显示不同的提示信息
if (NeedAuthorization.includes(feature.value)) {
// 不需要授权书的产品
showToast({ message: `请阅读并同意用户协议和隐私政策` });
} else if (needSeparateAuthorization.value) {
// 已经有单独的授权书勾选,这里只提示用户协议和隐私政策
showToast({ message: `请阅读并同意用户协议和隐私政策` });
} else {
// 常规情况:需要授权书但不需要单独勾选
showToast({
message: `请阅读并同意用户协议、隐私政策和授权书同意书`,
});
}
return;
}
// 特殊API需要单独验证授权书勾选状态
if (needSeparateAuthorization.value && !agreeToAuthorization.value) {
showToast({ message: `请阅读并同意授权书同意书和他人授权承诺书` });
return;
}
// 欺诈赌博查询的特殊验证
if (feature.value === "toc_FraudGamblingCheck" || feature.value === "toc_MobileDrugFraudRiskCheck") {
if (fraudGamblingCheckType.value === "mobile" && !isPhoneNumberValid.value) {
if (
feature.value === "toc_FraudGamblingCheck" ||
feature.value === "toc_MobileDrugFraudRiskCheck"
) {
if (
fraudGamblingCheckType.value === "mobile" &&
!isPhoneNumberValid.value
) {
showToast({ message: "请输入有效的手机号" });
return;
} else if (fraudGamblingCheckType.value === "idCard" && !isIdCardValid.value) {
} else if (
fraudGamblingCheckType.value === "idCard" &&
!isIdCardValid.value
) {
showToast({ message: "请输入有效的身份证号码" });
return;
} else if (fraudGamblingCheckType.value === "bankCard" && !isBankCardValid.value) {
} else if (
fraudGamblingCheckType.value === "bankCard" &&
!isBankCardValid.value
) {
showToast({ message: "请输入有效的银行卡号码" });
return;
}
@@ -299,7 +365,12 @@ function handleSubmit() {
// 常规验证逻辑
if (
!validateField("name", name.value, (v) => v, "请输入姓名") ||
!validateField("nameMan", nameMan.value, (v) => v, "请输入男方姓名") ||
!validateField(
"nameMan",
nameMan.value,
(v) => v,
"请输入男方姓名"
) ||
!validateField(
"nameWoman",
nameWoman.value,
@@ -354,19 +425,19 @@ function handleSubmit() {
(v) => isCarLicense.value,
"请输入正确的车牌号"
) ||
!validateField("entName", entName.value, (v) => v, "请输入企业名称") ||
!validateField(
"entName",
entName.value,
(v) => v,
"请输入企业名称"
) ||
!validateField(
"entCode",
entCode.value,
(v) => isCreditCodeValid.value,
"请输入统一社会信用代码"
) ||
!validateField(
"date",
dateVal.value,
(v) => v,
"请选择日期"
) ||
!validateField("date", dateVal.value, (v) => v, "请选择日期") ||
!validateField(
"certificateNumber",
certificateNumber.value,
@@ -425,6 +496,8 @@ const specialProduct = {
toc_EducationVerification: ["name", "idCard", "certificateNumber"],
toc_FraudGamblingCheck: ["fraudGamblingCheckType"], // 添加欺诈赌博查询,只用这个特殊字段标记
toc_MobileDrugFraudRiskCheck: ["fraudGamblingCheckType"], // 药品欺诈风险查询,使用相同的单选框逻辑
toc_DebtRepayStress: ["name", "idCard", "mobile"],
toc_EducationInfo: ["name", "idCard"],
//车辆
toc_PersonVehicleVerification: ["name", "carType", "carLicense"], // 人车核验
@@ -435,13 +508,14 @@ const specialProduct = {
toc_chassisNumberCheck: ["vinCode"], // 车辆车架号查车
toc_vehicleTransferCount: ["vinCode"], // 车辆过户次数
};
const NeedAuthorization = [
"toc_Marriage"
];
const NeedAuthorization = ["toc_Marriage"];
const isHasInput = (input) => {
if (specialProduct[feature.value]) {
// 欺诈赌博查询的特殊处理
if (feature.value === "toc_FraudGamblingCheck" || feature.value === "toc_MobileDrugFraudRiskCheck") {
if (
feature.value === "toc_FraudGamblingCheck" ||
feature.value === "toc_MobileDrugFraudRiskCheck"
) {
if (input === "fraudGamblingCheckType") {
return true; // 让单选框组显示
}
@@ -459,7 +533,10 @@ const isHasInput = (input) => {
async function submitRequest() {
const req = {};
// 针对欺诈赌博查询和药品欺诈风险查询的特殊处理
if (feature.value === "toc_FraudGamblingCheck" || feature.value === "toc_MobileDrugFraudRiskCheck") {
if (
feature.value === "toc_FraudGamblingCheck" ||
feature.value === "toc_MobileDrugFraudRiskCheck"
) {
// 只添加用户选择的那个字段
if (fraudGamblingCheckType.value === "mobile") {
req.mobile = mobile.value;
@@ -504,7 +581,7 @@ async function submitRequest() {
req.car_license = carLicense.value.trim();
}
if (isHasInput("date")) {
req.start_date = startDate.value.map(item => item).join('')
req.start_date = startDate.value.map((item) => item).join("");
}
if (isHasInput("entName")) {
req.ent_name = entName.value;
@@ -522,7 +599,7 @@ async function submitRequest() {
req.car_driving_permit = carDrivingPermit.value;
}
}
console.log("req", req)
console.log("req", req);
const reqStr = JSON.stringify(req);
const encodeData = aesEncrypt(reqStr, "ff83609b2b24fc73196aac3d3dfb874f");
const { data, error } = await useApiFetch(`/query/service/${feature.value}`)
@@ -571,15 +648,18 @@ function startCountdown() {
}, 1000);
}
function toUserAgreement() {
router.push(`/userAgreement`)
router.push(`/userAgreement`);
}
function toPrivacyPolicy() {
router.push(`/privacyPolicy`)
router.push(`/privacyPolicy`);
}
function toAuthorization() {
router.push(`/authorization`)
router.push(`/authorization`);
}
function toOthersAuthorization() {
router.push(`/othersAuthorization`);
}
// 用户同意
const agreed = () => {
@@ -593,7 +673,7 @@ const cancel = () => {
showAuthorizationPopup.value = false;
};
const toExample = () => {
router.push(`/example?feature=${feature.value}`)
router.push(`/example?feature=${feature.value}`);
};
onUnmounted(() => {
if (timer) {
@@ -612,24 +692,41 @@ onUnmounted(() => {
<!-- 欺诈赌博查询的单选框组 - 移动到输入框前面并改为并排按钮样式 -->
<div class="mb-6" v-if="isHasInput('fraudGamblingCheckType')">
<div class="mb-2 text-sm font-medium text-gray-700">请选择查询对象</div>
<div class="mb-2 text-sm font-medium text-gray-700">
请选择查询对象
</div>
<div class="flex space-x-2">
<div @click="fraudGamblingCheckType = 'mobile'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors" :class="fraudGamblingCheckType === 'mobile' ?
'bg-blue-500 text-white' :
'bg-gray-100 text-gray-600 hover:bg-gray-200'">
<div
@click="fraudGamblingCheckType = 'mobile'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors"
:class="
fraudGamblingCheckType === 'mobile'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
"
>
手机号
</div>
<div @click="fraudGamblingCheckType = 'idCard'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors" :class="fraudGamblingCheckType === 'idCard' ?
'bg-blue-500 text-white' :
'bg-gray-100 text-gray-600 hover:bg-gray-200'">
<div
@click="fraudGamblingCheckType = 'idCard'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors"
:class="
fraudGamblingCheckType === 'idCard'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
"
>
身份证号
</div>
<div @click="fraudGamblingCheckType = 'bankCard'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors" :class="fraudGamblingCheckType === 'bankCard' ?
'bg-blue-500 text-white' :
'bg-gray-100 text-gray-600 hover:bg-gray-200'">
<div
@click="fraudGamblingCheckType = 'bankCard'"
class="flex-1 rounded-md py-1 px-2 text-center text-sm cursor-pointer transition-colors"
:class="
fraudGamblingCheckType === 'bankCard'
? 'bg-blue-500 text-white'
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
"
>
银行卡号
</div>
</div>
@@ -637,120 +734,278 @@ onUnmounted(() => {
<div class="mb-4 flex items-center" v-if="isHasInput('name')">
<label for="name" class="form-label">姓名</label>
<input v-model="name" id="name" type="text" placeholder="请输入姓名" class="form-input" />
<input
v-model="name"
id="name"
type="text"
placeholder="请输入姓名"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('idCard')">
<label for="idCard" class="form-label">身份证号</label>
<input v-model="idCard" id="idCard" type="text" placeholder="请输入身份证号" class="form-input" />
<input
v-model="idCard"
id="idCard"
type="text"
placeholder="请输入身份证号"
class="form-input"
/>
</div>
<!-- 双人婚姻 -->
<div class="mb-4 flex items-center" v-if="isHasInput('nameMan')">
<label for="nameMan" class="form-label">男方姓名</label>
<input v-model="nameMan" id="nameMan" type="text" placeholder="请输入男方姓名" class="form-input" />
<input
v-model="nameMan"
id="nameMan"
type="text"
placeholder="请输入男方姓名"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('idCardMan')">
<label for="idCardMan" class="form-label">男方身份证号</label>
<input v-model="idCardMan" id="idCardMan" type="text" placeholder="请输入男方身份证号" class="form-input" />
<input
v-model="idCardMan"
id="idCardMan"
type="text"
placeholder="请输入男方身份证号"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('nameWoman')">
<label for="nameWoman" class="form-label">女方姓名</label>
<input v-model="nameWoman" id="nameWoman" type="text" placeholder="请输入女方姓名" class="form-input" />
<input
v-model="nameWoman"
id="nameWoman"
type="text"
placeholder="请输入女方姓名"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('idCardWoman')">
<div
class="mb-4 flex items-center"
v-if="isHasInput('idCardWoman')"
>
<label for="idCardWoman" class="form-label">女方身份证号</label>
<input v-model="idCardWoman" id="idCardWoman" type="text" placeholder="请输入女方身份证号" class="form-input" />
<input
v-model="idCardWoman"
id="idCardWoman"
type="text"
placeholder="请输入女方身份证号"
class="form-input"
/>
</div>
<!-- 双人婚姻 -->
<div class="mb-4 flex items-center" v-if="isHasInput('entName')">
<label for="entName" class="form-label">企业名称</label>
<input v-model="entName" id="entName" type="text" placeholder="请输入企业名称" class="form-input" />
<input
v-model="entName"
id="entName"
type="text"
placeholder="请输入企业名称"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('entCode')">
<label for="entCode" class="form-label">统一社会信用代码</label>
<input v-model="entCode" id="entCode" type="text" placeholder="请输入统一社会信用代码" class="form-input" />
<input
v-model="entCode"
id="entCode"
type="text"
placeholder="请输入统一社会信用代码"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('carType')">
<label for="carType" class="form-label">汽车类型</label>
<van-field id="carType" v-model="carType" is-link readonly placeholder="点击选择汽车类型"
@click="showCarTypePicker = true" class="form-input" />
<van-popup v-model:show="showCarTypePicker" destroy-on-close round position="bottom">
<van-picker :model-value="carPickerVal" :columns="carTypeColumns"
@cancel="showCarTypePicker = false" @confirm="onConfirmCarType" />
<van-field
id="carType"
v-model="carType"
is-link
readonly
placeholder="点击选择汽车类型"
@click="showCarTypePicker = true"
class="form-input"
/>
<van-popup
v-model:show="showCarTypePicker"
destroy-on-close
round
position="bottom"
>
<van-picker
:model-value="carPickerVal"
:columns="carTypeColumns"
@cancel="showCarTypePicker = false"
@confirm="onConfirmCarType"
/>
</van-popup>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('carLicense')">
<label for="carLicense" class="form-label !w-12">车牌号</label>
<CarNumberInput class="form-input" @number-input-result="carLicenseChange" :default-str="carLicense">
<CarNumberInput
class="form-input"
@number-input-result="carLicenseChange"
:default-str="carLicense"
>
</CarNumberInput>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('bankCard')">
<label for="bankCard" class="form-label">银行卡号</label>
<input v-model="bankCard" id="bankCard" type="tel" placeholder="请输入银行卡号" class="form-input" />
<input
v-model="bankCard"
id="bankCard"
type="tel"
placeholder="请输入银行卡号"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('mobile')">
<label for="mobile" class="form-label">手机号</label>
<input v-model="mobile" id="mobile" type="tel" placeholder="请输入手机号" class="form-input" />
<input
v-model="mobile"
id="mobile"
type="tel"
placeholder="请输入手机号"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('date')">
<label for="date" class="form-label">业务日期</label>
<van-field id="date" v-model="dateVal" is-link readonly placeholder="点击选择日期"
@click="showDatePicker = true" class="form-input" />
<van-popup v-model:show="showDatePicker" destroy-on-close round position="bottom">
<van-date-picker v-model="startDate" :formatter="formatterDate" :min-date="minDate"
:max-date="maxDate" title="选择日期" @confirm="onConfirmDate" @cancel="showDatePicker = false" />
<van-field
id="date"
v-model="dateVal"
is-link
readonly
placeholder="点击选择日期"
@click="showDatePicker = true"
class="form-input"
/>
<van-popup
v-model:show="showDatePicker"
destroy-on-close
round
position="bottom"
>
<van-date-picker
v-model="startDate"
:formatter="formatterDate"
:min-date="minDate"
:max-date="maxDate"
title="选择日期"
@confirm="onConfirmDate"
@cancel="showDatePicker = false"
/>
</van-popup>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('certificateNumber')">
<label for="certificateNumber" class="form-label">证书编号</label>
<input v-model="certificateNumber" id="certificateNumber" type="text" placeholder="请输入证书编号"
class="form-input" />
<div
class="mb-4 flex items-center"
v-if="isHasInput('certificateNumber')"
>
<label for="certificateNumber" class="form-label"
>证书编号</label
>
<input
v-model="certificateNumber"
id="certificateNumber"
type="text"
placeholder="请输入证书编号"
class="form-input"
/>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('vinCode')">
<label for="vinCode" class="form-label">车架号</label>
<input v-model="vinCode" id="vinCode" type="text" placeholder="请输入17位车架号" class="form-input"
maxlength="17" />
<input
v-model="vinCode"
id="vinCode"
type="text"
placeholder="请输入17位车架号"
class="form-input"
maxlength="17"
/>
</div>
<div class="mb-4" v-if="isHasInput('CarDrivingPermit')">
<div class="mb-2">
<label class="form-label block mb-2">行驶证照片</label>
<div class="relative w-full h-40 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center bg-gray-50 cursor-pointer overflow-hidden"
@click="$refs.fileInput.click()">
<div
class="relative w-full h-40 border-2 border-dashed border-gray-300 rounded-lg flex flex-col items-center justify-center bg-gray-50 cursor-pointer overflow-hidden"
@click="$refs.fileInput.click()"
>
<!-- 未上传时显示提示 -->
<template v-if="!carDrivingPermitFile">
<svg class="mx-auto h-12 w-12 text-gray-400" stroke="currentColor" fill="none"
viewBox="0 0 48 48">
<svg
class="mx-auto h-12 w-12 text-gray-400"
stroke="currentColor"
fill="none"
viewBox="0 0 48 48"
>
<path
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
<p class="mt-2 text-sm text-gray-600">点击或拖拽上传行驶证照片</p>
<p class="mt-1 text-xs text-gray-500">(支持JPGPNG格式)</p>
<p class="mt-2 text-sm text-gray-600">
点击或拖拽上传行驶证照片
</p>
<p class="mt-1 text-xs text-gray-500">
(支持JPGPNG格式)
</p>
</template>
<!-- 上传后显示图片预览 -->
<img v-else :src="`data:image/jpeg;base64,${carDrivingPermit}`"
class="absolute inset-0 w-full h-full object-contain" alt="行驶证照片" />
<img
v-else
:src="`data:image/jpeg;base64,${carDrivingPermit}`"
class="absolute inset-0 w-full h-full object-contain"
alt="行驶证照片"
/>
</div>
<input ref="fileInput" type="file" accept="image/*" @change="handleCarDrivingPermitUpload"
class="hidden" />
<input
ref="fileInput"
type="file"
accept="image/*"
@change="handleCarDrivingPermitUpload"
class="hidden"
/>
</div>
<div v-if="carDrivingPermitFile" class="mt-2 text-xs text-gray-500 flex justify-between">
<div
v-if="carDrivingPermitFile"
class="mt-2 text-xs text-gray-500 flex justify-between"
>
<span>文件: {{ carDrivingPermitFile.name }}</span>
<span>{{ (carDrivingPermitFile.size / 1024).toFixed(2) }}KB</span>
<span
>{{
(carDrivingPermitFile.size / 1024).toFixed(2)
}}KB</span
>
</div>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('verificationCode')">
<div
class="mb-4 flex items-center"
v-if="isHasInput('verificationCode')"
>
<label for="verificationCode" class="form-label">验证码</label>
<div class="flex-1 flex items-center">
<input v-model="verificationCode" id="verificationCode" type="text" placeholder="请输入验证码"
class="form-input flex-1" />
<button class="ml-2 px-4 py-2 text-sm text-blue-500 disabled:text-gray-400"
:disabled="isCountingDown || !isPhoneNumberValid" @click="sendVerificationCode">
<input
v-model="verificationCode"
id="verificationCode"
type="text"
placeholder="请输入验证码"
class="form-input flex-1"
/>
<button
class="ml-2 px-4 py-2 text-sm text-blue-500 disabled:text-gray-400"
:disabled="isCountingDown || !isPhoneNumberValid"
@click="sendVerificationCode"
>
{{
isCountingDown
? `${countdown}s重新获取`
@@ -764,18 +1019,52 @@ onUnmounted(() => {
<input type="checkbox" v-model="agreeToTerms" />
<span class="ml-2 text-xs text-gray-400">
我已阅读并同意
<span @click="toUserAgreement" class="text-blue-500 ">用户协议</span>
<span @click="toPrivacyPolicy" class="text-blue-500 ">隐私政策</span>
<template v-if="!NeedAuthorization.includes(feature)">
<span @click="toAuthorization" class="text-blue-500 ">授权书</span>
<span @click="toUserAgreement" class="text-blue-500"
>用户协议</span
>
<span @click="toPrivacyPolicy" class="text-blue-500"
>隐私政策</span
>
<template
v-if="
!NeedAuthorization.includes(feature) &&
!needSeparateAuthorization
"
>
<span @click="toAuthorization" class="text-blue-500"
>《授权书同意书》</span
>
</template>
</span>
</div>
<!-- 特殊API需要单独勾选授权书 -->
<div
class="mb-4 flex items-center"
v-if="needSeparateAuthorization"
>
<input type="checkbox" v-model="agreeToAuthorization" />
<span class="ml-2 text-xs text-gray-400">
我已阅读并同意
<span @click="toAuthorization" class="text-blue-500"
>《授权书同意书》</span
>
<span @click="toOthersAuthorization" class="text-blue-500"
>《他人授权承诺书》</span
>
</span>
</div>
<div class="flex items-center">
<button class="w-24 rounded-l-xl bg-blue-400 py-2 text-white" @click="toExample">
<button
class="w-24 rounded-l-xl bg-blue-400 py-2 text-white"
@click="toExample"
>
示例报告
</button>
<button class="flex-1 rounded-r-xl bg-blue-500 py-2 text-white" @click="handleSubmit">
<button
class="flex-1 rounded-r-xl bg-blue-500 py-2 text-white"
@click="handleSubmit"
>
立即查询
</button>
</div>
@@ -790,25 +1079,42 @@ onUnmounted(() => {
<div class="text-2xl text-red-600 font-semibold">
¥{{ featureData.sell_price }}
</div>
</div>
</div>
<div class="mb-4 text-gray-600 leading-relaxed" v-html="featureData.description">
<div
class="mb-4 text-gray-600 leading-relaxed"
v-html="featureData.description"
></div>
<div class="mb-2 text-xs text-red-500 italic">
为保证用户的隐私以及数据安全查询的结果生成15天之后将自动清除。
</div>
<template v-if="featureData.features && featureData.features.length > 1">
<template
v-if="featureData.features && featureData.features.length > 1"
>
<div class="mb-4 text-lg text-gray-800 font-semibold">
报告包含内容
</div>
<div class="grid grid-cols-2 gap-4">
<div v-for="(feature, index) in featureData.features" :key="feature.id"
class="rounded-lg py-2 text-center text-sm text-gray-700 font-medium" :class="[
<div
class="rounded-lg py-2 text-center text-sm text-gray-700 font-medium bg-gradient-to-r from-blue-200 via-blue-200 to-blue-100"
>
分析指数
</div>
<div
class="rounded-lg py-2 text-center text-sm text-gray-700 font-medium bg-gradient-to-r from-sky-200 via-sky-200 to-sky-100"
>
基本信息
</div>
<div
v-for="(feature, index) in featureData.features"
:key="feature.id"
class="rounded-lg py-2 text-center text-sm text-gray-700 font-medium"
:class="[
(Math.floor(index / 2) + (index % 2)) % 2 === 0
? 'bg-gradient-to-r from-blue-200 via-blue-200 to-blue-100'
: 'bg-gradient-to-r from-sky-200 via-sky-200 to-sky-100',
]">
]"
>
{{ feature.name }}
</div>
</div>
@@ -816,11 +1122,26 @@ onUnmounted(() => {
</div>
</div>
<!-- 底部弹出 -->
<van-popup v-model:show="showAuthorizationPopup" position="bottom" :style="{ height: '80%' }">
<Authorization :style="{ height: '100%' }" :name="name" :id-card="idCard" :mobile="mobile" @agreed="agreed"
@cancel="cancel" />
<van-popup
v-model:show="showAuthorizationPopup"
position="bottom"
:style="{ height: '80%' }"
>
<Authorization
:style="{ height: '100%' }"
:name="name"
:id-card="idCard"
:mobile="mobile"
@agreed="agreed"
@cancel="cancel"
/>
</van-popup>
<Payment v-model="showPayment" :data="featureData" :id="queryId" @close="showPayment = false" />
<Payment
v-model="showPayment"
:data="featureData"
:id="queryId"
@close="showPayment = false"
/>
</template>
<style scoped>

View File

@@ -79,7 +79,7 @@ async function fetchUserInfo() {
if (data.value && !error.value) {
if (data.value.code === 200) {
const userinfo = data.value.data.userInfo
userName.value = userinfo.nickName || ''
userName.value = userinfo.mobile || ''
userAvatar.value = userinfo.userAvatar || 'https://img0.baidu.com/it/u=1240274933,2284862568&fm=253&fmt=auto&app=138&f=PNG?w=180&h=180'
isLoggedIn.value = true
}

View File

@@ -0,0 +1,85 @@
<template>
<!-- 授权书滚动区域 -->
<div class="card flex-1 overflow-y-auto p-4">
<p class="text-center text-xl font-bold mb-2">他人授权同意书</p>
<p class="text-center text-lg font-bold text-red-500 my-2">
(未成年人不得使用)
</p>
<p class="indent-[2em]">
我本人确认我此次查询的司法文书数据可能涉及除了我本人以外的其他人的内容对此我本人承诺我已经获得其他主体的授权该授权可以是书面或口头形式
</p>
<p class="mt-2 font-bold">授权内容如下</p>
<ol class="list-decimal pl-6">
<li>
我确认已获得被授权人的明确授权允许我代表其查询与其相关的司法文书信息
</li>
<li>
我确认已向被授权人充分说明查询目的范围及可能产生的后果并获得其知情同意
</li>
<li>
我有权代替授权人在贵平台查询可能与授权人有关的公开的法律文书信息
</li>
<li>
我点击"同意"即视为明示表达我已获得他人授权并对此承担相应的法律责任
</li>
<li>
本授权有效期为自授权之日起1个月此授权为不可撤销授权但法律法规另有规定的除外
</li>
</ol>
<p class="mt-2 font-bold">用户声明与承诺</p>
<ul class="list-decimal pl-6">
<li>
本人是完全民事行为能力人我完全理解并同意上述所有内容并且是真实意思表达
</li>
<li>
如果我违反上述内容弄虚作假我本人承担一切可能的法律责任如果给平台造成损失我应赔偿
</li>
<li>
我确认已妥善保管被授权人的授权证明并可在必要时向相关方提供证明材料
</li>
<li>
我承诺仅将查询结果用于合法正当目的不会用于任何可能侵害他人合法权益的活动
</li>
</ul>
<p class="mt-2 font-bold">特别提示</p>
<ul class="list-decimal pl-6">
<li>
平台重要提醒已公开的司法数据未必有您本人(或委托人)的信息请知悉!
</li>
<li>
本服务是虚拟数据服务一经提交查询则会产生后台信息服务
</li>
<li>
平台对用户提供的授权真实性不承担审核责任但保留在发现授权存疑时要求用户提供证明的权利
</li>
<li>
平台声明本授权书涉及的信息核实及查询结果由第三方服务商提供平台不对数据的准确性完整性实时性承担责任用户根据报告所作决策的风险由用户自行承担平台对此不承担法律责任
</li>
<li>
本授权书中涉及的数据查询和报告生成由依法成立的第三方服务商提供若因第三方行为导致数据错误或损失用户应向第三方主张权利平台不承担相关责任
</li>
</ul>
<p class="mt-2 font-bold">争议解决机制</p>
<ul>
<li>
若因本授权书引发争议双方应友好协商解决协商不成的双方同意将争议提交至授权书签署地广西壮族自治区有管辖权的人民法院解决
</li>
</ul>
<p class="mt-2 font-bold">签署方式的法律效力声明</p>
<ul>
<li>
本授权书通过用户在线勾选电子签名或其他网络签署方式完成与手写签名具有同等法律效力平台已通过技术手段保存签署过程的完整记录作为用户真实意思表示的证据
</li>
</ul>
<p class="mt-2">本授权书于 {{ formatDate(new Date()) }}生效</p>
</div>
</template>
<script setup>
// 仅保留日期格式化函数用于显示时间
const formatDate = (date) => {
const options = { year: "numeric", month: "long", day: "numeric" };
return new Intl.DateTimeFormat("zh-CN", options).format(date);
};
</script>
<style scoped></style>

File diff suppressed because it is too large Load Diff

View File

@@ -2,151 +2,195 @@
const router = useRouter();
function toInquire(name) {
if (name === 'Marriage') {
router.push(`/inquire/toc_Marriage`);
// window.location.href = 'https://www.quannengcha.com?_um_campaign=67bfea379a16fe6dcd53b9a7&_um_channel=67bfea379a16fe6dcd53b9a8';
} else if (name === 'Risk') {
window.location.href = 'https://www.tianyuandb.com?_um_campaign=67c15d4f8f232a05f1221b82&_um_channel=67c15d4f8f232a05f1221b83';
} else if (name === 'Api') {
window.location.href = 'https://www.tianyuanapi.com/';
}
else {
router.push(`/list${name}`,);
}
if (name === "Marriage") {
router.push(`/inquire/marriage`);
// window.location.href = 'https://www.quannengcha.com?_um_campaign=67bfea379a16fe6dcd53b9a7&_um_channel=67bfea379a16fe6dcd53b9a8';
} else if (name === "Api") {
window.location.href = "https://www.tianyuanapi.com/";
} else {
router.push(`/list${name}`);
}
}
// 页面加载时生成 30 条通知数据
onMounted(() => {
})
function toHistory() {
router.push(`/historyQuery`);
router.push(`/historyQuery`);
}
function toPromote() {
window.location.href =
"https://www.tianyuandb.com?_um_campaign=67c15d4f8f232a05f1221b82&_um_channel=67c15d4f8f232a05f1221b83"; // 跳转到客服页面
}
</script>
<template>
<div class="relative p-4">
<img class="h-full w-full overflow-hidden rounded-xl" src="@/assets/images/banner.png" />
</div>
<div class="relative p-4 pb-4 pt-2">
<div class="grid grid-cols-2 gap-3">
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Car')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_index_chacheliang.svg" />
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
查车辆
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
车辆出险信息车辆维保记录人车关系名下车辆车架号查车等
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Marriage')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
婚恋报告
</div>
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_1.png" />
</div>
<div class="max-w-max text-left text-xs text-gray-600">
核查个人不良婚姻状态是否未婚已婚离婚离婚冷静期
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Lawsuit')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_2.png" />
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
司法涉诉
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
查询个人与企业的民事案件刑事案件行政案件非诉保全审查执行案件强制清算与破产等司法涉诉记录
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Verify')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
核验工具
</div>
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_4.png" />
</div>
<div class="max-w-max text-left text-xs text-gray-600">
核验身份证手机银行卡自然人生存状态学历人车关系等的查询工具
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Risk')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_3.png" />
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
风险报告
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
基于多维度数据整合准确评估潜在风险的全面报告
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Api')">
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold">
API商店
</div>
<img class="h-12 w-12 flex-shrink-0" src="@/assets/images/icon_index_apistore.svg" />
</div>
<div class="max-w-max text-left text-xs text-gray-600">
提供婚姻司法核验等高效便捷的API服务帮助企业快速获取数据提升业务效率
</div>
</div>
</div>
<div class="relative p-4">
<img
class="h-full w-full overflow-hidden rounded-xl"
src="@/assets/images/banner.png"
/>
</div>
<div class="mt-4 font-bold">
更多服务
</div>
<div class="mt-4 box-border h-14 w-full flex items-center rounded-xl bg-white px-4 text-gray-700 shadow-xl"
@click="toHistory">
<img class="mr-4 h-10 w-10" src="@/assets/images/bg_icon.png" mode="widthFix" />
<div class="">
<div class="font-bold">
我的历史查询记录
<div class="relative p-4 pb-4 pt-2">
<div class="grid grid-cols-2 gap-3">
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Lawsuit')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_2.png"
/>
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
司法涉诉
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
查询个人与企业的民事案件刑事案件行政案件非诉保全审查执行案件强制清算与破产限高失信等司法涉诉记录
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Marriage')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
婚恋风险
</div>
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_1.png"
/>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
全面评估婚恋对象潜在风险包含个人不良司法涉诉婚姻状态借贷行为等多维数据分析帮您识别隐藏风险守护情感安全
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Risk')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_3.png"
/>
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
风险报告
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
基于多维度数据整合准确评估潜在风险的全面报告
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Verify')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
核验工具
</div>
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_4.png"
/>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
核验身份证手机银行卡自然人生存状态学历人车关系等的查询工具
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-[35px] rounded-br-lg rounded-tl-[35px] rounded-tr-lg bg-white px-4 py-6 shadow-lg"
@click="toInquire('Car')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_index_chacheliang.svg"
/>
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
查车辆
</div>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
车辆出险信息车辆维保记录人车关系名下车辆车架号查车等
</div>
</div>
</div>
<div
class="relative flex flex-col cursor-pointer rounded-bl-lg rounded-br-[35px] rounded-tl-lg rounded-tr-[35px] bg-white px-4 py-6 shadow-lg"
@click="toInquire('Api')"
>
<div class="min-h-18 gap-2 bg-white px-1">
<div class="mb-2 flex justify-around">
<div
class="mt-1 max-w-max flex-shrink-0 text-left text-lg text-gray-600 font-bold"
>
API商店
</div>
<img
class="h-12 w-12 flex-shrink-0"
src="@/assets/images/icon_index_apistore.svg"
/>
</div>
<div class="max-w-max text-left text-xs text-gray-600">
提供婚姻司法核验等高效便捷的API服务帮助企业快速获取数据提升业务效率
</div>
</div>
</div>
</div>
<div class="text-xs">
查询记录有效期为30天
<div class="mt-4 font-bold">更多服务</div>
<div
class="mt-4 box-border h-14 w-full flex items-center rounded-xl bg-white px-4 text-gray-700 shadow-xl"
@click="toHistory"
>
<img
class="mr-4 h-10 w-10"
src="@/assets/images/bg_icon.png"
mode="widthFix"
/>
<div class="">
<div class="font-bold">我的历史查询记录</div>
<div class="text-xs">查询记录有效期为15天</div>
</div>
</div>
<div class="shadow-3xl mt-8 w-full" @click="toPromote">
<img
class="shadow-3xl w-full overflow-hidden rounded-xl"
src="@/assets/images/liu.png"
mode="widthFix"
/>
</div>
</div>
</div>
<div class="shadow-3xl mt-8 w-full">
<img class="shadow-3xl w-full overflow-hidden rounded-xl" src="@/assets/images/liu.png" mode="widthFix" />
</div>
</div>
</template>
<style scoped>
.clip-left {
clip-path: polygon(0 0, 0 100%, 90% 100%, 0 100%);
clip-path: polygon(0 0, 0 100%, 90% 100%, 0 100%);
}
.clip-right {
clip-path: polygon(0 0, 0 0, 90% 100%, 0 0);
clip-path: polygon(0 0, 0 0, 90% 100%, 0 0);
}
</style>

View File

@@ -1,66 +1,111 @@
<template>
<div class="bg-gradient-to-b from-[#aeceff] to-white min-h-screen relative">
<!-- banner -->
<div class="w-full flex justify-center " style="clip-path: ellipse(100% 95% at 50% 0%);">
<img src="@/assets/images/sfss_banner.png" alt="司法涉诉" class="w-full h-auto">
<div
class="w-full flex justify-center"
style="clip-path: ellipse(100% 95% at 50% 0%)"
>
<img
src="@/assets/images/sfss_banner.png"
alt="司法涉诉"
class="w-full h-auto"
/>
</div>
<div class="p-6 -my-24 z-1000 absolute">
<div class="card">
<div class="grid grid-cols-3 gap-6">
<div v-for="(item, index) in menuItems" :key="index" class="flex flex-col items-center"
@click="toInquire(item.product)">
<div
v-for="(item, index) in menuItems"
:key="index"
class="flex flex-col items-center"
@click="toInquire(item.product)"
>
<div class="bg-slate-100 rounded-full p-4">
<img :src="item.icon" :alt="item.title" class="w-10 h-10">
<img
:src="item.icon"
:alt="item.title"
class="w-10 h-10"
/>
</div>
<p class="mt-2 text-sm font-semibold">{{ item.title }}</p>
<p class="mt-2 text-sm text-center font-semibold">
{{ item.title }}
</p>
</div>
</div>
</div>
<div class="card mt-4">
<h2 class="text-lg font-bold text-blue-500 mb-4">司法涉诉查询服务</h2>
<h2 class="text-lg font-bold text-blue-500 mb-4">
司法涉诉查询服务
</h2>
<p class="text-gray-700 leading-6">
本平台提供全方位司法涉诉查询服务助您全面防范功能包括
</p>
<ul class="list-disc list-inside mt-4 space-y-2 text-gray-600">
<li>
<strong>个人涉诉</strong> 查看个人是否涉及诉讼失信限高案件了解其法律纠纷情况帮助您判断该个人的法律风险
<strong>个人涉诉</strong>
查看个人是否涉及诉讼失信限高案件了解其法律纠纷情况帮助您判断该个人的法律风险
</li>
<li>
<strong>企业涉诉</strong> 查看企业是否涉及诉讼失信限高案件了解企业的法律风险及潜在的经营问题帮助您评估与该企业合作的安全性
<strong>企业涉诉</strong>
查看企业是否涉及诉讼失信限高案件了解企业的法律风险及潜在的经营问题帮助您评估与该企业合作的安全性
</li>
<li>
<strong>个人失信</strong>个人限高功能帮助您查询个人是否被法院采取限制高消费措施了解其是否因未履行法院判决而受到出行消费等方面的限制通过该功能您可以评估该个人的履约能力和信用状况帮助您在商业合作雇佣关系或其他交易场景中规避潜在风险做出更加明智的决策
<strong>个人失信</strong
>个人限高功能帮助您查询个人是否被法院采取限制高消费措施了解其是否因未履行法院判决而受到出行消费等方面的限制通过该功能您可以评估该个人的履约能力和信用状况帮助您在商业合作雇佣关系或其他交易场景中规避潜在风险做出更加明智的决策
</li>
<li>
<strong>个人限高</strong>
个人失信功能帮助您查看个人是否被列入失信被执行人名单了解其是否存在严重违约拒不履行生效法律文书确定义务的情况通过该功能您可以全面评估该个人的信用状况和诚信水平帮助您在融资合作或其他重要决策场景中识别潜在风险保护自身权益避免不必要的损失
</li>
<li>
<strong>股东人企关系</strong>
股东人企关系功能帮助您查看个人为哪些企业的股东了解其与该企业的关系以及企业所涉诉讼失信限高案件情况帮助您评估与该企业的合作风险
</li>
<li>
<strong>人企关系加强版</strong>
<p>
人企关系加强版功能通过查询个人关联的企业帮助您了解该个人可能涉及的风险特别是与高风险企业有联系的个人查询结果包括投资企业详情高管任职记录以及企业涉诉情况全面展示个人商业网络该功能帮助您评估个人的风险水平确保在合作过程中识别潜在的法律财务或信誉风险
</p>
</li>
</ul>
</div>
</div>
</div>
</template>
<script setup>
import grssIcon from '@/assets/images/grss_icon.svg';
import qyssIcon from '@/assets/images/qyss_icon.svg';
import sxIcon from '@/assets/images/sx_icon.svg';
import xgIcon from '@/assets/images/xg_icon.svg';
const router = useRouter()
import grssIcon from "@/assets/images/grss_icon.svg";
import qyssIcon from "@/assets/images/qyss_icon.svg";
import sxIcon from "@/assets/images/sx_icon.svg";
import xgIcon from "@/assets/images/xg_icon.svg";
import gdrqgxIcon from "@/assets/images/gdrqgx_icon.svg";
import rqgxProIcon from "@/assets/images/rqgxpro_icon.svg";
const router = useRouter();
const menuItems = [
{ title: "个人涉诉", icon: grssIcon, product: "toc_PersonalLawsuit" },
{ title: "企业涉诉", icon: qyssIcon, product: "toc_EnterpriseLawsuit" },
{ title: "个人失信", icon: sxIcon, product: "toc_PersonalDiscredit" },
{ title: "个人限高", icon: xgIcon, product: "toc_PersonalConsumptionRestriction" },
{
title: "个人限高",
icon: xgIcon,
product: "toc_PersonalConsumptionRestriction",
},
{
title: "股东人企关系",
icon: gdrqgxIcon,
product: "toc_ShareholderBusinessRelation",
},
{
title: "人企关系加强版",
icon: rqgxProIcon,
product: "toc_PersonEnterprisePro",
},
];
const toInquire = (product) => {
if (!product) return
router.push('/inquire/' + product)
}
if (!product) return;
router.push("/inquire/" + product);
};
</script>
<style scoped></style>

View File

@@ -1,53 +1,61 @@
<template>
<div class="bg-gradient-to-b from-[#aeceff] to-white min-h-screen relative">
<!-- banner -->
<div class="w-full flex justify-center " style="clip-path: ellipse(100% 95% at 50% 0%);">
<img src="@/assets/images/fxpg_banner.png" alt="风险评估" class="w-full h-auto">
<div class="w-full flex justify-center" style="clip-path: ellipse(100% 95% at 50% 0%)">
<img src="@/assets/images/fxpg_banner.png " alt="风险报告" class="w-full h-auto" />
</div>
<div class="p-6 -my-24 z-1000 absolute">
<!-- 功能菜单 -->
<div class="card">
<div class="grid grid-cols-3 gap-6">
<div v-for="(item, index) in menuItems" :key="index" class="flex flex-col items-center"
@click="toInquire(item.product)">
<div class="bg-slate-100 rounded-full p-4">
<img :src="item.icon" :alt="item.title" class="w-10 h-10">
<!-- 分类功能菜单 -->
<van-skeleton :row="5" :loading="isFetching">
<div v-for="(items, category) in categorizedMenuItems" :key="category" class="card mb-4">
<h2 class="text-lg font-bold text-blue-500 mb-3">{{ category }}</h2>
<div class="grid grid-cols-3 gap-4">
<div v-for="(item, index) in items" :key="index" class="flex flex-col items-center"
@click="toInquire(item.product)">
<div class="bg-slate-100 rounded-full p-4">
<img :src="item.icon" :alt="item.title" class="w-10 h-10" />
</div>
<p class="mt-2 text-sm text-center font-semibold">
{{ item.title }}
</p>
</div>
<p class="mt-2 text-sm font-semibold">{{ item.title }}</p>
</div>
</div>
</div>
</van-skeleton>
<!-- 详情文本 -->
<div class="card mt-4">
<h2 class="text-lg font-bold text-blue-500 mb-4">风险评估查询服务</h2>
<div class="card mt-4" v-if="Object.keys(categorizedMenuItems).length !== 0">
<h2 class="text-lg font-bold text-blue-500 mb-4">
风险报告服务
</h2>
<p class="text-gray-700 leading-6">
本平台提供全方位风险评估查询服务助您全面防范功能包括
本平台提供全方位风险报告服务助您全面防范风险功能包括
</p>
<ul class="list-disc list-inside mt-4 space-y-2 text-gray-600">
<li>
<strong>个人不良记录查询</strong> 通过查询个人的不良记录评估其风险等级高风险帮助您识别潜在的信用和法律风险确保合作方的合法合规
</li>
<li>
<strong>股东人企关系查询</strong> 通过个人关联的企业了解其可能涉及的风险帮助您判断该个人的风险水平特别是与高风险企业有联系的个人
</li>
</ul>
<van-skeleton :row="3" :loading="isFetching">
<div v-for="(items, category) in categorizedMenuItems" :key="category" class="mt-4">
<h3 class="font-bold text-md text-gray-700 mb-2">{{ category }}</h3>
<ul class="list-disc list-inside space-y-2 text-gray-600">
<li v-for="(item, index) in items" :key="index">
<strong>{{ item.title }}</strong>
{{ item.description }}
</li>
</ul>
</div>
</van-skeleton>
</div>
</div>
</div>
</template>
<script setup>
import grblIcon from '@/assets/images/grbl_icon.svg';
import gdrqgxIcon from '@/assets/images/gdrqgx_icon.svg';
const router = useRouter()
const menuItems = [
{ title: "个人不良", icon: grblIcon, product: "toc_PersonalBadRecord" },
{ title: "股东人企关系", icon: gdrqgxIcon, product: "toc_ShareholderBusinessRelation" },
];
const router = useRouter();
const { fetchRenderData, categorizedMenuItems, isFetching } = useMenuItems();
const toInquire = (product) => {
router.push('/inquire/' + product)
}
router.push("/inquire/" + product);
};
onMounted(() => {
fetchRenderData("risk");
});
</script>

View File

@@ -18,11 +18,13 @@ export default defineConfig({
target: 'https://www.tianyuancha.cn', // 本地接口地址
changeOrigin: true,
},
"/api/v1/chat": {
target: "https://www.tianyuancha.cn", // 本地接口地址
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/v1\/chat/, '/chat')
},
// "/api/v1": {
// target: "https://6m4685017o.goho.co", // 本地接口地址
// changeOrigin: true,
@@ -45,7 +47,7 @@ export default defineConfig({
resolvers: [VantResolver()],
}),
vueJsx(),
// vueDevTools(),
vueDevTools(),
],
resolve: {
alias: {