Compare commits

...

2 Commits

Author SHA1 Message Date
f37e0dd927 v1.0.0 2025-01-04 00:38:57 +08:00
d7d10f55bc v0.99 2024-12-28 00:05:20 +08:00
80 changed files with 4452 additions and 977 deletions

View File

@ -4,9 +4,14 @@
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全能查-专业防风险</title>
<script type="text/javascript" src="https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>全能查 - 婚恋评估、司法涉诉查询、婚姻状态、判决书查询工具</title>
<meta name="description"
content="全能查提供婚恋评估报告、司法涉诉查询、婚姻状态查询、判决书查询、失信人、个人涉诉、企业涉诉、车辆核验等多项服务,帮助您查询婚姻信息、名下车辆、涉诉风险等,提供全面的法律与金融风险防范工具。" />
<meta name="keywords"
content="婚恋评估, 司法涉诉查询, 判决书查询, 婚姻状态查询, 失信人, 个人涉诉查询, 企业涉诉查询, 名下车辆核验, 车辆核验, 婚姻报告, 法律风险, 信用风险, 银行卡黑名单, 手机身份证核验, 学历核验, AI律师" />
<meta name="author" content="全能查" />
<meta name="baidu-site-verification" content="4d551d55896a88badef8dcdb14cf874c" />
<style>
#app-loading {
position: fixed;

View File

@ -1,10 +1,43 @@
<script setup>
import { RouterLink, RouterView } from 'vue-router'
import WechatOverlay from './components/WechatOverlay.vue';
onMounted(() => {
RefreshToken()
})
const RefreshToken = async () => {
const token = localStorage.getItem("token")
const refreshAfter = localStorage.getItem("refreshAfter")
const currentTime = new Date().getTime();
// 1. token
if (!token) {
return;
}
// 2. refreshAfter refreshAfterrefreshAfter
if (refreshAfter) {
const refreshAfterInMilliseconds = parseInt(refreshAfter) * 1000; //
if (currentTime < refreshAfterInMilliseconds) {
return;
}
}
// 3. refreshAfter refreshAfter token
const { data, error } = await useApiFetch("/user/getToken")
.post()
.json();
if (data.value && !error.value) {
if (data.value !== 200) {
localStorage.setItem("token", data.value.data.accessToken);
localStorage.setItem("refreshAfter", data.value.data.refreshAfter);
}
}
}
</script>
<template>
<WechatOverlay />
<RouterView />
</template>

View File

@ -5,8 +5,12 @@
margin: 0;
font-weight: normal;
}
html {
margin: auto !important;
@apply max-w-lg;
}
body {
background-color: #f8f8f8;
min-height: 100vh;
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/assets/images/bg_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
src/assets/images/liu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
src/assets/images/llqdk.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 507 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
src/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 1024 1024" height="1024" width="1024">
<g clip-path="url(#clip0_1513_110780)">
<ellipse fill="url(#paint0_linear_1513_110780)" ry="127.5" rx="467" cy="762.5" cx="506"></ellipse>
<path fill="url(#paint1_linear_1513_110780)" d="M230.457 615.502C230.457 615.502 230.233 614.922 229.928 615.542C227.184 621.122 202.334 681.566 200.362 728.218C200.362 728.218 196.215 755.02 230.965 754.665C259.973 754.37 259.2 729.662 259.099 721.337C258.702 687.573 230.457 615.502 230.457 615.502Z"></path>
<path fill="url(#paint2_linear_1513_110780)" d="M229.664 784.343C229.105 784.343 228.647 783.886 228.647 783.327V631.754C228.647 631.195 229.105 630.737 229.664 630.737C230.223 630.737 230.68 631.195 230.68 631.754V783.327C230.68 783.896 230.223 784.343 229.664 784.343Z"></path>
<path fill="url(#paint3_linear_1513_110780)" d="M869.35 810.661C876.541 808.4 883.517 805.446 889.874 797.176C891.178 795.482 892.404 793.311 891.689 791.283C891.151 789.757 889.594 788.778 888.013 788.529C887.424 788.442 886.827 788.426 886.234 788.48H886.228C885.316 788.574 884.415 788.754 883.536 789.015L883.285 789.089C879.09 790.299 875.093 792.13 871.425 794.524C871.093 794.738 870.698 794.833 870.306 794.792C869.914 794.75 869.548 794.576 869.266 794.296C868.984 794.017 868.804 793.649 868.754 793.252C868.704 792.855 868.789 792.453 868.993 792.111C869.367 791.491 869.803 790.911 870.292 790.38C871.015 789.613 871.807 788.916 872.657 788.299C873.788 787.462 874.994 786.715 876.174 785.951C880.562 783.116 884.719 779.929 888.605 776.42C891.378 773.913 894.091 771.131 895.409 767.607C896.015 765.986 896.291 764.117 895.568 762.547C894.376 759.968 890.999 759.245 888.237 759.78C883.398 760.721 879.532 764.32 876.104 767.908C873.261 770.889 870.075 774.189 865.99 774.372C865.294 774.439 864.594 774.296 863.977 773.963C862.812 773.247 862.512 771.588 862.927 770.271C863.343 768.953 864.304 767.901 865.231 766.89C870.623 761.021 875.928 754.916 879.429 747.725C882.93 740.534 884.481 732.044 882.048 724.409C881.335 722.177 880.263 720.005 878.591 718.383C876.919 716.762 874.575 715.745 872.28 716.056C869.153 716.473 866.798 719.15 865.213 721.915C859.768 731.401 860.101 743.025 858.567 753.891C858.138 756.933 857.514 760.053 855.728 762.557C855.563 762.798 855.35 763 855.101 763.15C853.632 764.013 852.313 762.272 851.761 760.795C849.583 754.95 849.038 748.265 845.139 743.427C843.65 741.585 841.544 740.046 839.197 740.061C836.331 740.081 833.877 742.51 833.04 745.292C832.204 748.074 832.705 751.096 833.601 753.858C835.264 758.98 838.245 763.582 841.759 767.627C843.61 769.763 845.705 771.92 846.261 774.706C846.431 775.653 846.36 776.628 846.054 777.539C845.748 778.45 845.218 779.267 844.513 779.912C844.184 780.202 843.801 780.423 843.386 780.561C842.971 780.699 842.533 780.75 842.098 780.713C841.157 780.626 840.306 780.139 839.484 779.666C839.139 779.467 838.794 779.265 838.447 779.067C837.077 778.276 835.707 777.483 834.336 776.69C830.851 774.674 826.646 772.614 823.018 774.35C821.167 775.238 819.954 776.983 819.383 778.986C818.872 780.804 818.872 782.73 819.383 784.548C820.464 788.328 827.276 798.848 830.086 801.561C833.368 804.729 844.748 818.398 869.35 810.661Z"></path>
<path fill="url(#paint4_linear_1513_110780)" d="M649.638 740.684C650.38 738.632 650.88 736.312 651.021 733.581C651.097 732.138 650.946 730.46 649.772 729.604C648.888 728.959 647.648 729.008 646.674 729.474C646.314 729.65 645.975 729.869 645.667 730.126L645.663 730.129C645.193 730.529 644.761 730.974 644.373 731.455L644.261 731.592C642.394 733.869 640.875 736.417 639.754 739.152C639.651 739.399 639.468 739.602 639.235 739.729C639.001 739.856 638.73 739.899 638.467 739.851C638.203 739.802 637.962 739.666 637.782 739.464C637.603 739.262 637.495 739.006 637.478 738.737C637.448 738.249 637.468 737.759 637.537 737.276C637.644 736.573 637.817 735.882 638.053 735.213C638.302 734.488 638.6 733.776 638.897 733.067C638.968 732.898 639.038 732.73 639.108 732.561C640.46 729.305 641.548 725.941 642.363 722.501C642.944 720.045 643.385 717.458 642.767 714.99C642.483 713.856 641.92 712.709 640.917 712.11C639.267 711.129 637.113 712.017 635.783 713.37C633.455 715.744 632.686 719.226 632.155 722.535C631.718 725.282 631.212 728.339 629.013 730.001C628.651 730.304 628.208 730.492 627.737 730.543C626.815 730.589 626.013 729.779 625.739 728.886C625.465 727.994 625.596 727.04 625.724 726.122C626.471 720.792 627.08 715.364 626.27 710.019C625.46 704.675 623.069 699.352 618.792 696.028C617.541 695.056 616.113 694.256 614.563 693.991C613.012 693.727 611.32 694.056 610.164 695.106C608.586 696.534 608.303 698.925 608.482 701.071C608.942 706.634 611.7 711.617 614.449 716.583C615.34 718.193 616.23 719.801 617.041 721.426C617.968 723.285 618.816 725.262 618.783 727.34C618.784 727.537 618.743 727.731 618.662 727.909C618.177 728.952 616.777 728.486 615.904 727.874C614.781 727.084 613.72 726.179 612.661 725.276C610.467 723.405 608.281 721.54 605.57 720.728C604.037 720.27 602.277 720.217 600.979 721.122C599.394 722.229 598.961 724.52 599.562 726.389C600.163 728.259 601.599 729.751 603.155 730.948C606.042 733.166 609.462 734.591 612.964 735.502C613.144 735.549 613.325 735.595 613.507 735.641C615.193 736.072 616.935 736.516 618.178 737.726C618.635 738.189 618.969 738.759 619.148 739.384C619.328 740.008 619.346 740.666 619.201 741.294C619.13 741.582 619.001 741.852 618.824 742.087C618.646 742.322 618.422 742.518 618.166 742.664C617.61 742.975 616.95 743.029 616.312 743.079C616.226 743.086 616.14 743.092 616.053 743.099C615.871 743.113 615.689 743.126 615.507 743.142C614.442 743.224 613.378 743.306 612.312 743.388C609.603 743.596 606.477 744.055 605.127 746.409C604.438 747.611 604.433 749.047 604.883 750.381C605.296 751.589 606.034 752.662 607.014 753.48C608.305 754.547 611.561 756.243 614.443 757.461C618.506 760.98 630.194 760.946 644.502 760.905C646.078 760.9 647.685 760.896 649.318 760.896C650.95 760.896 652.558 760.9 654.133 760.905C671.968 760.956 685.732 760.996 685.732 754.158C685.732 746.756 669.602 740.748 649.638 740.684Z" clip-rule="evenodd" fill-rule="evenodd"></path>
<path fill="#E0EFFB" d="M398.378 606.003C398.378 552.319 432.932 506.743 480.916 490.497C432.932 474.257 398.378 428.681 398.378 374.997V340.028H370.877C361.556 340.028 354 332.438 354 323.074V320.955C354 311.591 361.556 304 370.877 304H668.935C678.256 304 685.812 311.591 685.812 320.954V323.074C685.812 332.438 678.256 340.028 668.935 340.028H640.982V374.997C640.982 428.681 606.427 474.257 558.444 490.503C606.427 506.743 640.982 552.319 640.982 606.003V640.972H669.123C678.444 640.972 686 648.562 686 657.926V660.045C686 669.409 678.444 677 669.123 677H371.065C361.744 677 354.189 669.409 354.189 660.046V657.926C354.189 648.562 361.744 640.972 371.065 640.972H398.378V606.003Z"></path>
<path fill="#81C2FA" d="M613 637C613 673.451 565.362 656 514 656C462.638 656 427 673.451 427 637C427 600.549 467.638 557 519 557C570.362 557 613 600.549 613 637Z"></path>
<path fill="url(#paint5_linear_1513_110780)" d="M668.668 304C678.011 304 685.585 311.591 685.585 320.954V323.074C685.585 332.438 678.011 340.028 668.668 340.028H369.916C360.574 340.028 353 332.438 353 323.074V320.955C353 311.591 360.574 304 369.916 304H668.668V304ZM669.084 640.972C678.426 640.972 686 648.562 686 657.926V660.045C686 669.409 678.426 677 669.084 677H370.331C360.989 677 353.415 669.409 353.415 660.046V657.926C353.415 648.562 360.989 640.972 370.331 640.972H669.084V640.972Z"></path>
<ellipse fill-opacity="0.1" fill="#7D7D7D" ry="15.5" rx="56.5" cy="744.5" cx="380.5"></ellipse>
<path fill="url(#paint6_linear_1513_110780)" d="M388.138 590.092H362.863V615.366H388.138V590.092Z"></path>
<path fill="#FEE0BC" d="M402.423 582.636C404.608 583.518 407.573 581.276 409.047 577.628C410.521 573.98 409.946 570.307 407.761 569.424C405.577 568.542 402.611 570.784 401.137 574.432C399.663 578.08 400.239 581.753 402.423 582.636Z"></path>
<path fill="#FEE0BC" d="M348.175 582.595C346.006 583.516 343.001 581.326 341.464 577.704C339.926 574.082 340.438 570.4 342.607 569.479C344.776 568.559 347.78 570.749 349.318 574.37C350.855 577.992 350.343 581.675 348.175 582.595Z"></path>
<path fill="url(#paint7_linear_1513_110780)" d="M376.181 600.131C392.089 600.131 404.984 587.403 404.984 571.702C404.984 556.001 392.089 543.272 376.181 543.272C360.274 543.272 347.378 556.001 347.378 571.702C347.378 587.403 360.274 600.131 376.181 600.131Z"></path>
<path fill="url(#paint8_linear_1513_110780)" d="M404.119 573.743C414.43 559.996 409.275 525.629 374.907 525.629C340.54 525.629 333.667 561.714 347.414 573.743C352.568 558.278 398.964 558.278 404.119 573.743Z"></path>
<path fill="#585449" d="M410.191 557.658C409.554 572.537 393.77 555.533 374.3 555.533C354.829 555.533 337.984 574.237 338.408 555.533C338.408 535.812 355.466 521.95 374.937 521.95C394.407 521.95 411.034 537.955 410.191 557.658Z"></path>
<path fill="#747169" d="M408.724 562.119C408.724 565.75 393.233 564.876 374.123 564.876C355.013 564.876 339.521 565.75 339.521 562.119C339.521 558.487 355.013 552.998 374.123 552.998C393.233 552.998 408.724 558.487 408.724 562.119Z"></path>
<path fill="url(#paint9_linear_1513_110780)" d="M386.491 606.175C387.05 605.65 387.608 605.068 388.164 604.428L388.174 604.432C388.384 604.433 388.64 604.475 389.128 604.824C391.595 605.916 393.875 607.539 395.959 609.51C419.437 620.293 448.133 651.929 449.682 659.957C451.485 669.295 447.834 674.286 444.137 676.077C442.286 676.974 429.317 681.461 415.201 685.27C416.83 704.65 418.718 723.386 420.606 728.15C421.865 731.327 412.177 732.54 398.561 732.695C371.08 735.371 324.372 734.768 326.993 728.154C328.875 723.404 330.757 704.767 332.382 685.448C320.048 682.577 309.039 679.178 307.296 678.307C303.692 676.505 296.482 669.295 298.285 658.481C299.675 650.138 334.524 615.04 357.975 605.055C358.455 604.827 358.941 604.619 359.435 604.432L359.454 604.453C359.491 604.439 359.528 604.425 359.564 604.411C359.567 604.426 359.57 604.441 359.572 604.455C361.418 603.115 362.636 602.608 362.931 602.608C365.835 606.378 371.949 611.363 378.778 610.25C381.883 609.361 384.592 607.826 386.491 606.175ZM334.655 656.861C334.874 654.069 335.081 651.452 335.276 649.074C332.93 650.984 330.236 652.93 327.122 654.877C329.488 655.215 332.023 655.912 334.655 656.861ZM413.042 658.12C412.851 655.675 412.669 653.353 412.495 651.194C414.72 652.904 417.239 654.639 420.105 656.375C417.878 656.683 415.505 657.292 413.042 658.12Z" clip-rule="evenodd" fill-rule="evenodd"></path>
<path fill="#FB955B" d="M391.434 605.947C391.434 605.947 389.643 604.093 387.853 604.093C378.206 614.08 367.28 607.14 362.782 602.24C362.613 602.048 360.991 602.238 359.2 604.093C374.959 618.925 387.256 611.509 391.434 605.947Z"></path>
<path fill="#FEE4C3" d="M334.332 746.796L331.538 734.637L317.4 737.47L319.526 748.074L334.332 746.796Z"></path>
<path fill="#7B7B7B" d="M321.766 736.133C318.964 740.512 320.757 746.368 322.003 748.748C318.467 752.42 303.692 755.992 303.692 746.802C303.691 737.784 312.722 734.501 321.766 736.133Z"></path>
<path fill="#FEE4C3" d="M415.233 746.796L418.027 734.637L432.165 737.47L430.04 748.074L415.233 746.796Z"></path>
<path fill="#7B7B7B" d="M427.798 736.133C430.6 740.512 428.808 746.368 427.561 748.748C431.097 752.42 445.872 755.992 445.873 746.802C445.873 737.784 436.842 734.501 427.798 736.133Z"></path>
<path fill="#DCDCDC" d="M426.623 696.181C426.623 696.181 446.29 692.531 452.543 706.678C457.11 723.22 440.502 727.377 440.502 727.377L427.51 729.924L421.852 749.064L375.903 740.046L329.917 749.064L324.251 729.908L311.397 727.387C311.397 727.387 293 720.449 299.37 706.697C307.428 689.3 325.262 696.204 325.262 696.204L375.881 710.561L426.623 696.181Z"></path>
<path fill="url(#paint10_linear_1513_110780)" d="M330.263 637.979C330.263 637.426 330.71 636.979 331.263 636.979H415.708C416.26 636.979 416.708 637.426 416.708 637.979V698.15H330.263V637.979Z"></path>
<rect fill="url(#paint11_linear_1513_110780)" rx="2" height="5.49441" width="94.1376" y="698.149" x="326.967"></rect>
<path fill="url(#paint12_linear_1513_110780)" d="M774.5 291.455C776.513 291.455 778.444 292.254 779.868 293.678C781.291 295.101 782.091 297.032 782.091 299.045V329.409C782.091 331.422 781.291 333.353 779.868 334.777C778.444 336.2 776.513 337 774.5 337C772.487 337 770.556 336.2 769.132 334.777C767.709 333.353 766.909 331.422 766.909 329.409V299.045C766.909 297.032 767.709 295.101 769.132 293.678C770.556 292.254 772.487 291.455 774.5 291.455V291.455ZM747.659 280.341C749.082 281.765 749.881 283.695 749.881 285.708C749.881 287.721 749.082 289.651 747.659 291.075L726.191 312.542C725.491 313.267 724.654 313.845 723.727 314.243C722.801 314.641 721.805 314.85 720.797 314.859C719.789 314.868 718.79 314.676 717.857 314.294C716.924 313.913 716.077 313.349 715.364 312.636C714.651 311.923 714.087 311.076 713.706 310.143C713.324 309.21 713.132 308.211 713.141 307.203C713.15 306.195 713.359 305.199 713.757 304.273C714.155 303.346 714.733 302.509 715.458 301.809L736.925 280.341C738.349 278.918 740.279 278.119 742.292 278.119C744.305 278.119 746.235 278.918 747.659 280.341V280.341ZM812.075 280.341L833.542 301.809C834.925 303.24 835.69 305.158 835.673 307.148C835.655 309.138 834.857 311.042 833.45 312.45C832.042 313.857 830.138 314.655 828.148 314.673C826.158 314.69 824.24 313.925 822.809 312.542L801.341 291.075C799.959 289.643 799.194 287.726 799.211 285.736C799.228 283.745 800.027 281.841 801.434 280.434C802.841 279.027 804.745 278.228 806.736 278.211C808.726 278.194 810.643 278.959 812.075 280.341ZM728.955 245.909C730.968 245.909 732.899 246.709 734.322 248.132C735.746 249.556 736.545 251.487 736.545 253.5C736.545 255.513 735.746 257.444 734.322 258.868C732.899 260.291 730.968 261.091 728.955 261.091H698.591C696.578 261.091 694.647 260.291 693.223 258.868C691.8 257.444 691 255.513 691 253.5C691 251.487 691.8 249.556 693.223 248.132C694.647 246.709 696.578 245.909 698.591 245.909H728.955ZM850.409 245.909C852.422 245.909 854.353 246.709 855.777 248.132C857.2 249.556 858 251.487 858 253.5C858 255.513 857.2 257.444 855.777 258.868C854.353 260.291 852.422 261.091 850.409 261.091H820.045C818.032 261.091 816.101 260.291 814.678 258.868C813.254 257.444 812.455 255.513 812.455 253.5C812.455 251.487 813.254 249.556 814.678 248.132C816.101 246.709 818.032 245.909 820.045 245.909H850.409ZM833.542 194.458C834.965 195.881 835.765 197.812 835.765 199.825C835.765 201.838 834.965 203.768 833.542 205.191L812.075 226.659C810.643 228.041 808.726 228.806 806.736 228.789C804.745 228.772 802.841 227.973 801.434 226.566C800.027 225.159 799.228 223.255 799.211 221.264C799.194 219.274 799.959 217.357 801.341 215.925L822.809 194.458C824.232 193.035 826.162 192.235 828.175 192.235C830.188 192.235 832.119 193.035 833.542 194.458V194.458ZM726.191 194.458L747.659 215.925C748.384 216.625 748.962 217.463 749.36 218.389C749.758 219.315 749.967 220.311 749.976 221.319C749.984 222.327 749.792 223.327 749.411 224.259C749.029 225.192 748.465 226.04 747.753 226.753C747.04 227.465 746.192 228.029 745.259 228.411C744.327 228.792 743.327 228.984 742.319 228.976C741.311 228.967 740.315 228.758 739.389 228.36C738.463 227.962 737.625 227.384 736.925 226.659L715.458 205.191C714.075 203.76 713.31 201.842 713.327 199.852C713.345 197.862 714.143 195.958 715.55 194.55C716.958 193.143 718.862 192.345 720.852 192.327C722.842 192.31 724.76 193.075 726.191 194.458V194.458ZM774.5 170C776.513 170 778.444 170.8 779.868 172.223C781.291 173.647 782.091 175.578 782.091 177.591V207.955C782.091 209.968 781.291 211.899 779.868 213.322C778.444 214.746 776.513 215.545 774.5 215.545C772.487 215.545 770.556 214.746 769.132 213.322C767.709 211.899 766.909 209.968 766.909 207.955V177.591C766.909 175.578 767.709 173.647 769.132 172.223C770.556 170.8 772.487 170 774.5 170Z"></path>
</g>
<defs>
<linearGradient gradientUnits="userSpaceOnUse" y2="890" x2="506" y1="635" x1="506" id="paint0_linear_1513_110780">
<stop stop-color="#BDDFFF"></stop>
<stop stop-opacity="0" stop-color="white" offset="0.95892"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="615.257" x2="229.66" y1="754.678" x1="229.66" id="paint1_linear_1513_110780">
<stop stop-color="#5FB2FF"></stop>
<stop stop-color="#DFF0FF" offset="0.7893"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="784.347" x2="229.661" y1="630.738" x1="229.661" id="paint2_linear_1513_110780">
<stop stop-color="#DFF0FF"></stop>
<stop stop-color="#A2D2FF" offset="0.818"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="802.968" x2="847.534" y1="720.042" x1="883.842" id="paint3_linear_1513_110780">
<stop stop-color="#C4E3FF" offset="0.365159"></stop>
<stop stop-color="#5FB2FF" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="696.911" x2="605.494" y1="778.74" x1="649.318" id="paint4_linear_1513_110780">
<stop stop-color="#97CDFF" offset="0.568288"></stop>
<stop stop-color="#5FB2FF" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="646.727" x2="206.755" y1="200" x1="686" id="paint5_linear_1513_110780">
<stop stop-color="#6CB8FF"></stop>
<stop stop-color="#B1D9FF" offset="0.865168"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="615.366" x2="375.5" y1="593.358" x1="375.5" id="paint6_linear_1513_110780">
<stop stop-color="#FFCDA5"></stop>
<stop stop-color="#FFE8D1" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="600.131" x2="376.181" y1="543.272" x1="376.181" id="paint7_linear_1513_110780">
<stop stop-color="#FFF2DF"></stop>
<stop stop-color="#FEE0BC" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="579.657" x2="358.148" y1="540.753" x1="396.189" id="paint8_linear_1513_110780">
<stop stop-color="#777CA7"></stop>
<stop stop-color="#636681" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="749.353" x2="384.874" y1="591.568" x1="425.773" id="paint9_linear_1513_110780">
<stop stop-color="#FFE194"></stop>
<stop stop-color="#FFBF84" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="690.962" x2="358.001" y1="640.104" x1="392.552" id="paint10_linear_1513_110780">
<stop stop-color="#94CBFF"></stop>
<stop stop-color="#DCEEFF" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="703.644" x2="374.036" y1="698.149" x1="374.036" id="paint11_linear_1513_110780">
<stop stop-color="#94CBFF"></stop>
<stop stop-color="#DCEEFF" offset="1"></stop>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" y2="304.871" x2="701.011" y1="178.533" x1="811.334" id="paint12_linear_1513_110780">
<stop stop-color="#6CB8FF"></stop>
<stop stop-color="#BDDFFF" offset="0.781833"></stop>
</linearGradient>
<clipPath id="clip0_1513_110780">
<rect fill="white" height="1024" width="1024"></rect>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -24,3 +24,6 @@ a,
.card {
@apply shadow rounded-xl bg-white p-6;
}
.ltitle {
@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;
}

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,8 @@
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const VanTabbar: typeof import('vant/es')['Tabbar']
const VanTabbarItem: typeof import('vant/es')['TabbarItem']
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
const closeToast: typeof import('vant/es')['closeToast']

View File

@ -64,7 +64,7 @@
本人有权随时撤回本授权书中的授权但撤回前的授权行为及其法律后果仍具有法律效力若需撤回授权本人可通过贵司官方渠道提交书面申请贵司将在收到申请后依法停止对本人数据的使用
</li>
<li>
你通过全能查APP或推广方推广查询模式自愿支付相应费用用于购买海南省学宇思网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
你通过全能查自愿支付相应费用用于购买海南省学宇思网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
</li>
<li>
你向海南省学宇思网络科技有限公司的支付方式为海南省学宇思网络科技有限公司及其经官方授权的相关企业的支付宝账户

File diff suppressed because one or more lines are too long

View File

@ -24,12 +24,12 @@
</template>
<script setup>
// Vue 3
const route = useRoute();
//
function goBack() {
unionuni.navigateBack({ delta: 1 })
route.goBack()
}
</script>

View File

@ -0,0 +1,71 @@
<template>
<div class="card flex flex-col items-center justify-center text-center">
<!-- 图片插画 -->
<img src="@/assets/images/pendding.svg" alt="查询中" class="w-64 h-64" />
<!-- 提示文字 -->
<h2 class="text-xl font-semibold text-gray-700 mb-2 floating-text">
报告正在查询中
</h2>
<p class="text-gray-500 text-sm mb-2 leading-relaxed">
请稍候我们正在为您查询报告查询过程可能需要一些时间
</p>
<p class="text-gray-400 text-xs mb-4">
您可以稍后刷新页面查看结果或之后访问历史报告列表查看
</p>
<p class="text-gray-400 text-xs mb-4">
如过久未查询成功请联系客服为您处理
</p>
<!-- 按钮组 -->
<div class="flex gap-4">
<!-- 刷新按钮 -->
<button @click="refreshPage"
class="px-6 py-2 text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition duration-300 ease-in-out">
刷新页面
</button>
<!-- 历史报告按钮 -->
<button @click="viewHistory"
class="px-6 py-2 text-white bg-gray-500 rounded-lg hover:bg-gray-600 transition duration-300 ease-in-out">
查看历史报告
</button>
</div>
</div>
</template>
<script setup>
const router = useRouter();
//
function refreshPage() {
location.reload(); //
}
//
function viewHistory() {
router.push({ path: '/historyQuery' }); // 'historyReports'
}
</script>
<style scoped>
@keyframes floatUpDown {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
/* 向上浮动 */
}
100% {
transform: translateY(0);
/* 返回原位 */
}
}
/* 给提示文字和其他需要浮动的元素添加动画 */
.floating-text {
animation: floatUpDown 3s ease-in-out infinite;
/* 动画持续3秒缓入缓出循环播放 */
}
</style>

View File

@ -45,7 +45,8 @@ const lineClass = computed(() => {
</div>
<!-- 左上角修饰 -->
<div class="absolute left-0 top-0 h-4 w-4 transform rounded-full bg-white shadow-md -translate-x-2 -translate-y-2" />
<div
class="absolute left-0 top-0 h-4 w-4 transform rounded-full bg-white shadow-md -translate-x-2 -translate-y-2" />
<!-- 分割线 -->
<div class="relative mt-1.5">
@ -54,5 +55,4 @@ const lineClass = computed(() => {
</div>
</template>
<style scoped>
</style>
<style scoped></style>

View File

@ -5,14 +5,27 @@
<h3 class="text-lg font-bold">支付</h3>
</div>
<div class="text-center">
<p class="text-base">{{ data.product_name }}</p>
<p class="text-3xl text-red-500 font-bold">¥ {{ data.sell_price }}</p>
<div class="font-bold text-xl">{{ data.product_name }}</div>
<div class="text-3xl text-red-500 font-bold">
<!-- 显示原价和折扣价格 -->
<div v-if="discountPrice" class="line-through text-gray-500 mt-4"
:class="{ 'text-2xl': discountPrice }">
¥ {{ data.sell_price }}
</div>
<div>
¥ {{ discountPrice ? (data.sell_price * 0.2).toFixed(2) : data.sell_price }}
</div>
</div>
<!-- 仅在折扣时显示活动说明 -->
<div v-if="discountPrice" class="text-sm text-red-500 mt-1">
活动价2折优惠
</div>
</div>
<!-- 支付方式选择 -->
<div class="">
<van-cell-group inset>
<!-- 支付宝支付 -->
<van-cell v-if="platform === 'h5'" title="支付宝支付" clickable @click="selectedPaymentMethod = 'alipay'">
<van-cell title="支付宝支付" clickable @click="selectedPaymentMethod = 'alipay'">
<template #icon>
<van-icon size="24" name="alipay" color="#00A1E9" class="mr-2" />
</template>
@ -20,20 +33,11 @@
<van-radio v-model="selectedPaymentMethod" name="alipay" />
</template>
</van-cell>
<!-- 微信支付 -->
<van-cell v-if="platform !== 'h5'" title="微信支付" clickable @click="selectedPaymentMethod = 'wechat'">
<template #icon>
<van-icon size="24" name="wechatpay" color="#1AAD19" class="mr-2" />
</template>
<template #right-icon>
<van-radio v-model="selectedPaymentMethod" name="wechat" />
</template>
</van-cell>
</van-cell-group>
</div>
<!-- 确认按钮 -->
<div class="">
<van-button class="w-full" round type="primary" @click="payment">确认支付</van-button>
<van-button class="w-full" round type="primary" @click="getPayment">确认支付</van-button>
</div>
</van-popup>
</template>
@ -53,13 +57,25 @@ const props = defineProps({
const show = defineModel()
const orderId = ref("")
const selectedPaymentMethod = ref('alipay')
const { platform } = useUni()
function onConfirmPayment(data) {
window.parent.postMessage({ type: "payment", data: data }, "*");
show.value = false
}
const discountPrice = ref(false) //
onMounted(() => {
// let m = localStorage.getItem("m")
// let hour = "12"
// if (m === "shifenliangzai") {
// hour = "00"
// }
// const currentDate = new Date()
// const startDate = new Date(`2025-01-01T${hour}:00:00+08:00`) // 20251112
// const endDate = new Date('2025-01-02T12:00:00+08:00') // 20251212
// console.log(startDate, endDate)
// if (currentDate >= startDate && currentDate <= endDate) {
// discountPrice.value = true //
// } else {
// discountPrice.value = false //
// }
})
async function payment() {
async function getPayment() {
const { data, error } = await useApiFetch('/pay/payment')
.post({
id: props.id,
@ -69,8 +85,15 @@ async function payment() {
if (data.value && !error.value) {
orderId.value = data.value.data.order_id
onConfirmPayment(data.value.data.prepay_id)
const prepayUrl = data.value.data.prepay_id;
const paymentForm = document.createElement('form');
paymentForm.method = 'POST';
paymentForm.action = prepayUrl;
paymentForm.style.display = 'none';
document.body.appendChild(paymentForm);
paymentForm.submit();
}
show.value = false
}
</script>

View File

@ -0,0 +1,72 @@
<template>
<div v-if="isWeChat" class="wechat-overlay">
<div class="wechat-content">
<p class="wechat-message">
点击右上角的<van-icon class="ml-2" name="weapp-nav" /><br />然后点击在浏览器中打开
</p>
<img src="@/assets/images/llqdk.jpg" alt="In WeChat" class="wechat-image" />
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
//
const isWeChat = ref(false);
//
const checkIfWeChat = () => {
const userAgent = navigator.userAgent.toLowerCase();
isWeChat.value = /micromessenger/.test(userAgent);
};
//
onMounted(() => {
checkIfWeChat();
});
</script>
<style scoped>
/* 遮罩层样式 */
.wechat-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
/* 遮罩中的内容 */
.wechat-content {
text-align: center;
color: white;
font-size: 16px;
}
/* 图片样式 */
.wechat-image {
/* position: absolute;
bottom: 0;
left: 0; */
margin-top: 20px;
width: 100%;
}
/* 提示信息的样式 */
.wechat-message {
font-size: 24px;
}
/* 图标样式 */
.icon-more-vert {
font-size: 20px;
margin-left: 5px;
}
</style>

View File

@ -1,8 +1,7 @@
// src/plugins/fetch.js
import { createFetch } from '@vueuse/core'
import router from '@/router' // 假设你使用 Vue Router
const { getCurrentPlatform, platform } = useUni() // 获取平台信息(如 "app", "h5" 等)
getCurrentPlatform()
// 创建全局的 fetch 实例
const useApiFetch = createFetch({
baseUrl: '/api/v1', // 你的 API 基础路径
@ -18,12 +17,13 @@ const useApiFetch = createFetch({
// 获取平台信息
const brand = "qnc"; // 固定的品牌信息
// 在请求前添加通用的 Header例如 Authorization
const token = localStorage.getItem('token')
if (token) {
options.headers = {
...options.headers,
'X-Platform': platform.value, // 添加平台信息
'X-Platform': 'h5', // 添加平台信息
'X-Brand': brand, // 添加品牌信息
Authorization: `${token}`,
}
@ -38,10 +38,7 @@ const useApiFetch = createFetch({
localStorage.removeItem('token')
// 跳转到登录页
router.push('/login')
uni.redirectTo({
url: '/pages/login',
})
router.replace('/login')
}
if (data.code !== 200) {
@ -52,19 +49,19 @@ const useApiFetch = createFetch({
return { data, response }
},
async onFetchError({ error, response }) {
console.log("error", error)
closeToast();
if (response.status === 401) {
// 清除本地存储的 token
localStorage.removeItem('token')
// 跳转到登录页
router.push('/login')
uni.redirectTo({
url: '/pages/login',
})
}
// 全局错误处理
router.replace('/login')
} else {
if (typeof error === 'string') {
showToast({ message: error });
}
}
return { error }
},
},

View File

@ -1,49 +0,0 @@
import { ref, onMounted } from 'vue'
export function useUni() {
const platform = ref('') // 存储当前的平台
// 判断是否在微信浏览器中
const checkWeixinBrowser = () => {
return /MicroMessenger/i.test(navigator.userAgent)
}
// 获取当前平台并判断环境
const getCurrentPlatform = async () => {
try {
uni.getEnv(env => {
if (env.h5) {
// 如果是 H5 环境,检查是否在微信浏览器中
if (checkWeixinBrowser()) {
platform.value = 'h5-weixin'
} else {
platform.value = 'h5' // 当前是 Web 平台
}
} else if (env['mp-weixin']) {
platform.value = 'mp-weixin' // 当前是 微信小程序平台
} else if (env.app) {
platform.value = 'app' // 当前是 App 平台
} else if (env['mp-qq']) {
platform.value = 'mp-qq' // 当前是 QQ 小程序平台
} else if (env['mp-alipay']) {
platform.value = 'mp-alipay' // 当前是 支付宝小程序平台
} else {
platform.value = 'unknown' // 未知平台
}
})
} catch (error) {
console.error('获取平台信息失败:', error)
platform.value = 'unknown'
}
}
// 在组件挂载时执行环境判断
onMounted(() => {
getCurrentPlatform()
})
return {
platform,
getCurrentPlatform,
}
}

View File

@ -1,22 +1,16 @@
import { ref, onMounted } from "vue";
import "@/assets/uni-webview"
export function useWebView() {
const platform = ref("");
const token = ref("");
// 检测环境并通知父窗口加载完毕
const handleBridgeReady = (platformName) => {
console.log("handleBridgeReady", platformName)
if (platformName.h5) {
window.parent.postMessage({ loaded: true }, "*");
const handleBridgeReady = () => {
if (platform.value) {
h5PostMessage("loaded", true);
}
};
const handlePaymentMessage = () => {
if (platformName.h5) {
window.parent.postMessage({ loaded: true }, "*");
}
}
// 获取 Token从 URL 中解析)
const getTokenFromUrl = () => {
@ -29,14 +23,92 @@ export function useWebView() {
return tokenFromUrl;
};
onMounted(() => {
console.log(" /MicroMessenger/i.test(navigator.userAgent)", /MicroMessenger/i.test(navigator.userAgent))
// 检测平台环境
uni.getEnv((env) => {
platform.value = env;
handleBridgeReady(platform.value);
});
// 封装 postMessage 方法
const postMessage = (data) => {
if (platform.value === "h5") {
h5PostMessage("postMessage", data);
} else if (uni && uni.webView.postMessage) {
uni.webView.postMessage(data);
} else {
console.error("uni.webView.postMessage is not available.");
}
};
const redirectTo = (data) => {
if (platform.value === "h5") {
h5PostMessage("redirectTo", data)
} else if (uni && uni.webView.redirectTo) {
// 非 H5 环境,调用 uni.webView.redirectTo
uni.webView.redirectTo(data);
} else {
console.error("uni.webView.redirectTo is not available.");
}
};
// 封装 navigateBack 方法
const navigateBack = (data) => {
if (platform.value === "h5") {
window.top.history.back();
// h5PostMessage("navigateBack", data)
} else if (uni && uni.webView.navigateBack) {
// 非 H5 环境,调用 uni.webView.navigateBack
uni.webView.navigateBack(data);
} else {
console.error("uni.webView.navigateBack is not available.");
}
};
// 封装 navigateTo 方法
const navigateTo = (data) => {
if (platform.value === "h5") {
// h5PostMessage("navigateTo", data)
window.top.location.href = "/app" + data.url
} else if (uni && uni.webView.navigateTo) {
uni.webView.navigateTo(data);
} else {
console.error("uni.webView.navigateTo is not available.");
}
};
const payment = (data) => {
if (platform.value === "h5") {
h5PostMessage("payment", data)
} else if (uni && uni.webView.navigateTo) {
// 非 H5 环境,调用 uni.webView.navigateTo
uni.webView.navigateTo(data);
} else {
console.error("uni.webView.navigateTo is not available.");
}
}
const getEnv = () => {
return new Promise((resolve, reject) => {
let env = localStorage.getItem(platform)
if (env) {
platform.value = env
resolve(env);
} else {
uni.webView.getEnv((env) => {
// 遍历 env 对象,找到值为 true 的键
const platformKey = Object.keys(env).find(key => env[key] === true);
platform.value = platformKey;
if (platformKey) {
resolve(platformKey); // 返回键名(如 'h5', 'mp-weixin' 等)
} else {
reject('未知平台');
}
});
}
});
};
onMounted(async () => {
try {
const envValue = await getEnv();
console.log("当前环境", envValue)
// 将返回的键名(如 'h5', 'mp-weixin')存储到 platform
handleBridgeReady();
} catch (error) {
console.error(error);
}
// 获取 Token
getTokenFromUrl();
});
@ -44,6 +116,15 @@ export function useWebView() {
return {
platform,
token,
handlePaymentMessage
getEnv,
redirectTo,
postMessage,
navigateTo,
navigateBack,
payment
};
}
const h5PostMessage = (action, data) => {
window.parent.postMessage({ action, data, messageId: generateUniqueId(action) }, "*");
}
const generateUniqueId = (action) => `msg_${action}_${new Date().getTime()}`;

View File

@ -0,0 +1,93 @@
<template>
<router-view />
<van-popup v-model:show="showPopup" round @click-overlay="onClickOverlay">
<div class="popup-content text-center p-8">
<div v-html="currentNotify?.content"></div>
<div class="flex justify-center">
<van-button type="primary" @click="showPopup = false" class="w-24">关闭</van-button>
</div>
</div>
</van-popup>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
//
const showPopup = ref(false)
const notify = ref([])
const currentNotify = ref(null)
//
const route = useRoute()
//
onMounted(() => {
getGlobalNotify()
})
//
const getGlobalNotify = async () => {
const { data, error } = await useApiFetch("/notification/list")
.get()
.json()
if (data.value && !error.value) {
if (data.value !== 200) {
notify.value = data.value.data.notifications
checkNotification() //
}
}
}
//
const isWithinTimeRange = (startTime, endTime) => {
const now = new Date()
//
const currentMinutes = now.getHours() * 60 + now.getMinutes()
// startTime endTime
const startParts = startTime.split(':').map(Number)
const endParts = endTime.split(':').map(Number)
const startMinutes = startParts[0] * 60 + startParts[1]
const endMinutes = endParts[0] * 60 + endParts[1]
// endTime startTime
if (endMinutes < startMinutes) {
// [startTime, 23:59:59] [00:00:00, endTime]
return currentMinutes >= startMinutes || currentMinutes < endMinutes
}
//
return currentMinutes >= startMinutes && currentMinutes <= endMinutes
}
// showPopup
const checkNotification = () => {
//
for (let notification of notify.value) {
//
const isTimeValid = isWithinTimeRange(notification.startTime, notification.endTime)
//
if (isTimeValid && notification.notificationPage === route.path) {
currentNotify.value = notification
showPopup.value = true
break //
}
}
}
//
watch(() => route.path, () => {
checkNotification() //
})
//
const onClickOverlay = () => {
showPopup.value = false
}
</script>
<style lang="scss" scoped></style>

139
src/layouts/HomeLayout.vue Normal file
View File

@ -0,0 +1,139 @@
<template>
<div class="home-layout min-h-screen flex flex-col">
<!-- Header -->
<div class="header">
<img class="logo" src="@/assets/images/logo.png" alt="Logo" />
<div class="title">全能查</div>
</div>
<!-- Content Area -->
<div class="content flex flex-col flex-1">
<router-view />
</div>
<!-- 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>
<!-- Complaint Button -->
<div @click="toComplaint" class="complaint-button">
<!-- <i class="icon-warning"></i> -->
<span>投诉</span>
</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>
</div>
</template>
<script setup>
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 menu = reactive([
{ title: '首页', icon: 'home-o', name: 'index' },
{ title: 'AI律师', icon: 'chat-o', name: 'ai' },
{ title: '我的', icon: 'user-o', name: 'me' },
]);
// Tabbar
onMounted(() => {
const currentPage = route.name; //
tabbar.value = currentPage;
});
const onClickOverlay = () => { }
//
const tabChange = (name) => {
router.push({ name }); // 使 Vue Router
};
//
const toComplaint = () => {
router.push({ name: 'complaint' }); // 使 Vue Router
};
</script>
<style scoped>
.home-layout {
background: linear-gradient(to bottom, #cfe0fa, #f4f8ff);
}
.header {
display: flex;
align-items: center;
background-color: white;
padding-left: 1rem;
padding-right: 1rem;
height: 3rem;
}
.logo {
width: 2rem;
height: 2rem;
margin-right: 0.75rem;
object-fit: contain;
}
.title {
font-size: 1.25rem;
font-weight: bold;
}
.content {
/* min-height: calc(100vh - 3rem); */
}
.complaint-button {
position: fixed;
bottom: 6rem;
right: 1rem;
background: linear-gradient(to bottom, #e24949, #e4827b);
border-radius: 1.5rem;
padding: 0.25rem 1rem;
color: white;
display: flex;
align-items: center;
cursor: pointer;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
}
.complaint-button i {
margin-right: 0.5rem;
}
.disclaimer {
/* margin-top: 24px; */
padding: 10px;
font-size: 12px;
color: #999;
text-align: center;
border-top: 1px solid #e0e0e0;
padding-bottom: 60px;
background: #ffffff;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<van-nav-bar fixed :border="false" placeholder :title="pageTitle" left-text="返回" left-arrow
@click-left="onClickLeft" />
<router-view />
</template>
<script setup>
import { ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
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('/')
} else {
router.back()
}
}
onMounted(() => {
})
//
watch(
() => route.meta.title,
(newTitle) => {
pageTitle.value = newTitle || '默认标题'
},
{ immediate: true }
)
</script>
<style lang="scss" scoped></style>

View File

@ -5,9 +5,7 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import 'vant/lib/index.css';
const app = createApp(App)
app.use(createPinia())
app.use(router)

View File

@ -1,74 +1,136 @@
import { createRouter, createWebHistory } from 'vue-router'
import NProgress from 'nprogress';
import GlobalLayout from '@/layouts/GlobalLayout.vue';
import HomeLayout from '@/layouts/HomeLayout.vue';
import PageLayout from '@/layouts/PageLayout.vue';
import index from '@/views/index.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: () => import('@/views/Home.vue'),
component: GlobalLayout, // 使用 Layout 作为父组件
children: [
{
path: '',
component: HomeLayout, // 使用 Layout 作为父组件
children: [
{
path: '',
name: 'index',
component: index,
},
{
path: 'ai',
name: 'ai',
component: () => import('@/views/Ai.vue'),
},
{
path: 'me',
name: 'me',
component: () => import('@/views/Me.vue'),
},
]
},
{
path: '',
component: PageLayout,
children: [
{
path: '/historyQuery',
name: 'history',
component: () => import('@/views/HistoryQuery.vue'),
meta: { title: '历史报告' },
},
{
path: '/service',
name: 'service',
component: () => import('@/views/Service.vue'),
meta: { title: '客服' },
},
{
path: '/complaint',
name: 'complaint',
component: () => import('@/views/Complaint.vue'),
meta: { title: '投诉' },
},
{
path: '/report',
name: 'report',
component: () => import('@/views/Report.vue'),
meta: { title: '报告结果' },
},
{
path: '/example',
name: 'example',
component: () => import('@/views/Example.vue'),
meta: { title: '示例报告' },
},
{
path: '/authorization',
name: 'authorization',
component: () => import('@/views/Authorization.vue'),
meta: { title: '授权书' },
},
{
path: '/privacyPolicy',
name: 'privacyPolicy',
component: () => import('@/views/PrivacyPolicy.vue'),
meta: { title: '隐私政策' },
},
{
path: '/userAgreement',
name: 'userAgreement',
component: () => import('@/views/UserAgreement.vue'),
meta: { title: '用户协议' },
},
{
path: '/listMarriage',
name: 'listMarriage',
component: () => import('@/views/list_marriage.vue'),
meta: { title: '婚姻查询' },
},
{
path: '/listRisk',
name: 'listRisk',
component: () => import('@/views/list_risk.vue'),
meta: { title: '风险查询' },
},
{
path: '/listLawsuit',
name: 'listLawsuit',
component: () => import('@/views/list_lawsuit.vue'),
meta: { title: '诉讼查询' },
},
{
path: '/listVerify',
name: 'listVerify',
component: () => import('@/views/list_verify.vue'),
meta: { title: '核验查询' },
},
{
path: '/inquire/:feature',
name: 'inquire',
component: () => import('@/views/Inquire.vue'),
meta: { title: '查询报告' },
},
],
},
]
},
{
path: '/login',
name: 'login',
component: () => import('@/views/Login.vue'),
},
// {
// path: '/home',
// name: 'home',
// component: () => import('@/views/Home.vue'),
// },
{
path: "/:pathMatch(.*)*",
name: "NotFound",

View File

@ -16,22 +16,23 @@ const riskLevel = ref("");
const riskLevelText = ref("");
const riskLevelClass = ref("");
const riskInfoList = ref([
{ level: "低风险", description: "涉稳、寻衅滋事", class: "span-lime-600" },
{
level: "无风险",
description: "无任何不良风险记录",
class: "text-green-600",
},
{ level: "低风险", description: "涉稳、寻衅滋事", class: "text-yellow-500" },
{
level: "中风险",
description: "吸毒、涉毒、犯罪前科",
class: "text-orange-400",
class: "text-orange-500",
},
{
level: "高风险",
description: "涉案人员、在逃、犯罪嫌疑人",
class: "text-red-500",
},
{
level: "无风险",
description: "无任何不良风险记录",
class: "text-green-600",
},
]);
//
@ -45,9 +46,17 @@ function getChartOption(level) {
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}",
@ -66,7 +75,7 @@ function getChartOption(level) {
fontWeight: "bold",
color: "#333",
},
data: [{ value, name: "风险等级" }],
data: [{ value, name: labelName }],
axisLine: {
lineStyle: {
width: 15,
@ -74,40 +83,33 @@ function getChartOption(level) {
shadowColor: "rgba(0, 0, 0, 0.3)", //
color: [
[
0.2,
0.25,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#10B981" },
{ offset: 1, color: "#34D399" },
]),
], //
[
0.4,
0.5,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#A3E635" },
{ offset: 1, color: "#D9F99D" },
{ offset: 0, color: "#FFC300" },
{ offset: 1, color: "#EAB308" },
]),
], //
[
0.6,
0.75,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#F97316" },
{ offset: 1, color: "#FDBA74" },
{ offset: 1, color: "#E68416" },
]),
], //
[
0.8,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#EF4444" },
{ offset: 1, color: "#FCA5A5" },
]),
], //
[
1,
new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#9CA3AF" },
{ offset: 1, color: "#E5E7EB" },
{ offset: 0, color: "#EF4444" },
{ offset: 1, color: "#FF6347" },
]),
], //
], //
],
},
},
@ -125,7 +127,7 @@ function getChartOption(level) {
},
title: {
offsetCenter: [0, "75%"], //
fontSize: 14,
fontSize: 20,
fontWeight: "bold",
},
detail: {

38
src/ui/CCAR061.vue Normal file
View File

@ -0,0 +1,38 @@
<template>
<div class="card">
<!-- 名下车辆信息展示 -->
<div class="bg-yellow-100 text-yellow-700 p-4 rounded-lg">
<h3 class="text-xl font-semibold">名下车辆</h3>
<p class="text-sm">此人名下拥有车辆{{ data?.carNum }} </p>
</div>
<!-- 校验对象展示 -->
</div>
</template>
<script setup>
import { defineProps } from 'vue';
// props
const props = defineProps({
data: Object,
params: Object,
});
//
const maskName = (name) => {
if (!name) return '';
return name.length > 1 ? name[0] + "*".repeat(name.length - 1) : "*";
};
// 64
const maskIdCard = (idCard) => {
if (!idCard) return '';
return idCard.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
};
</script>
<style scoped>
/* 自定义样式 */
</style>

81
src/ui/CDualMarriage.vue Normal file
View File

@ -0,0 +1,81 @@
<template>
<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>
<div
: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">
<p>{{ statusDescription(data.status) }}</p>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
},
});
//
const getStatusClass = (status) => {
const statusClassMapping = {
"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": "离婚冷静期",
};
return statusMapping[status] || "未知状态";
};
const statusDescription = (status) => {
const descriptionMapping = {
"1": "双方是夫妻关系,目前处于已婚状态。",
"2": "双方存在过婚姻关系,目前已离婚。",
"3": "双方目前处于离婚冷静期。",
"0": "双方不存在婚姻关系。",
};
return descriptionMapping[status] || "无详细描述。";
};
//
const maskValue = (value, type) => {
if (type === "name") {
//
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "id_card") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
}
return value;
};
const maskedParams = {
nameMan: maskValue(props.params.nameMan, "name"),
idCardMan: maskValue(props.params.idCardMan, "id_card"),
nameWoman: maskValue(props.params.nameWoman, "name"),
idCardWoman: maskValue(props.params.idCardWoman, "id_card"),
};
</script>
<style scoped></style>

49
src/ui/CFIN019.vue Normal file
View File

@ -0,0 +1,49 @@
<template>
<div class="p-4 card space-y-4">
<!-- 黑名单信息 -->
<div class="">
<!-- <LTitle title="银行卡黑名单" type="blue-green" class="mb-4"></LTitle> -->
<div class="space-y-4">
<div v-for="(value, key) in data" :key="key" class="flex justify-between items-center">
<div class="font-medium">{{ labelMap[key] }}</div>
<div
:class="['text-white rounded-lg font-bold py-1 px-2', { 'bg-red-500': value === '1', 'bg-green-500': value === '0' }]">
{{ value === '0' ? '未存在' : '被列入该黑名单' }}
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue'
// props
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
}
})
//
const labelMap = {
onlineBlack: "线上卡号黑名单",
offlineBlack: "线下卡号黑名单",
badCardHolder: "不良持卡人名单",
fraudTrans: "交易欺诈名单",
otherBlack: "其他卡号黑名单",
caseRelated: "涉案卡片"
}
</script>
<style scoped>
/* 添加需要的样式 */
</style>

65
src/ui/CG02BJ02.vue Normal file
View File

@ -0,0 +1,65 @@
<template>
<div class="card">
<!-- 手机在网时长状态展示 -->
<div class=" bg-blue-100 text-blue-700 p-4 rounded-lg ">
<h3 class="text-xl font-semibold">手机在网时长</h3>
<p class="text-lg font-medium">{{ getTimeDuration(data?.code) }}</p>
<p class="text-sm mt-2">运营商{{ getPhoneType(data?.phoneType) }}</p>
</div>
<!-- 手机号码展示 -->
</div>
</template>
<script setup>
import { defineProps } from 'vue';
// props
const props = defineProps({
data: Object,
params: Object,
});
//
const maskMobile = (mobile) => {
if (!mobile) return '';
return mobile.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
};
// code
const getTimeDuration = (code) => {
switch (code) {
case 1006:
return "0 - 3 个月";
case 1007:
return "3 - 6 个月";
case 1008:
return "6 - 12 个月";
case 1009:
return "12 - 24 个月";
case 1010:
return "24 个月以上";
default:
return "未知时长";
}
};
// phoneType
const getPhoneType = (phoneType) => {
switch (phoneType) {
case "CMCC":
return "中国移动";
case "CUCC":
return "中国联通";
case "CTCC":
return "中国电信";
default:
return "未知运营商";
}
};
</script>
<style scoped>
/* 自定义样式 */
</style>

50
src/ui/CG03HZ01.vue Normal file
View File

@ -0,0 +1,50 @@
<template>
<div class="card">
<!-- 手机号码展示 -->
<!-- 风险等级展示 -->
<div v-if="data?.filterType === '0'" class="bg-green-100 text-green-700 p-4 rounded-lg">
<h3 class="text-xl font-semibold">安全号码</h3>
<p class="text-sm">该手机号码为安全号码没有发现任何风险</p>
</div>
<div v-if="data?.filterType === '1'" class="bg-red-100 text-red-700 p-4 rounded-lg">
<h3 class="text-xl font-semibold">高危</h3>
<p class="text-sm">该手机号码存在较高风险请谨慎处理</p>
</div>
<div v-if="data?.filterType === '2'" class="bg-yellow-100 text-yellow-700 p-4 rounded-lg">
<h3 class="text-xl font-semibold">中危</h3>
<p class="text-sm">该手机号码存在一定风险请留意相关信息</p>
</div>
<div v-if="data?.filterType === '3'" class="bg-blue-100 text-blue-700 p-4 rounded-lg">
<h3 class="text-xl font-semibold">低危</h3>
<p class="text-sm">该手机号码风险较低但仍需关注</p>
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
// props
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
}
});
//
const maskMobile = (mobile) => {
if (!mobile) return '';
return mobile.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
};
</script>
<style scoped>
/* 自定义样式 */
</style>

60
src/ui/CG19BJ02.vue Normal file
View File

@ -0,0 +1,60 @@
<template>
<div class="card">
<!-- 手机号码展示 -->
<div class="pb-4">
<ul>
<li class="flex justify-between py-2 border-b">
<span class="font-medium">手机号码</span>
<span>{{ maskMobile(params?.mobile) }}</span>
</li>
<li class="flex justify-between py-2 border-b">
<span class="font-medium">业务日期</span>
<span>{{ formatDate(params?.startDate) }}</span>
</li>
</ul>
</div>
<!-- 二次卡状态展示 -->
<div v-if="data?.code === 1026" class="bg-green-100 text-gray-700 p-4 rounded-lg mb-6">
<h3 class="text-xl font-semibold">非二次卡</h3>
<p class="text-sm">该手机号码不是二次卡请注意核对</p>
</div>
<div v-if="data?.code === 1025" class="bg-red-500 text-white p-4 rounded-lg mb-6">
<h3 class="text-xl font-semibold">二次卡</h3>
<p class="text-sm">该手机号码是二次卡请注意核对</p>
</div>
</div>
</template>
<script setup>
import { defineProps } from 'vue';
// props
const props = defineProps({
data: Object,
params: Object,
});
props.data.code = 1025
//
const maskMobile = (mobile) => {
if (!mobile) return '';
return mobile.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
};
// YYYY-MM-DD
const formatDate = (dateString) => {
if (!dateString) return '';
// 8 YYYYMMDD
const year = dateString.slice(0, 4);
const month = dateString.slice(4, 6);
const day = dateString.slice(6, 8);
//
return `${year}-${month}-${day}`;
};
</script>
<style scoped>
/* 自定义样式 */
</style>

101
src/ui/CG20GZ01.vue Normal file
View File

@ -0,0 +1,101 @@
<template>
<div class="card">
<!-- 状态展示 -->
<div :class="stateClass" class="text-center rounded-lg py-2">
<h2 class="text-xl font-semibold mb-2">银行卡四要素校验</h2>
<p class="text-sm">{{ stateMessage }}</p>
</div>
</div>
</template>
<script setup>
import { defineProps, computed } from 'vue'
// props
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
}
})
//
const stateMessage = computed(() => {
const state = props.data.data.state
switch (state) {
case '1':
return '验证一致';
case '2':
return '验证不一致';
case '3':
return '异常情况';
default:
return '未知状态';
}
})
const stateClass = computed(() => {
const state = props.data.data.state
switch (state) {
case '1':
return 'bg-green-100 text-green-600';
case '2':
return 'bg-red-100 text-red-600';
case '3':
return 'bg-yellow-100 text-yellow-600';
default:
return 'bg-gray-100 text-gray-600';
}
})
//
const maskValue = (value, type) => {
if (!value) return ''; //
if (type === "name") {
//
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "id_card") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "mobile") {
//
return value.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
} else if (type === "bank_card") {
//
return value.replace(/^(\d{4})(?:\d+)(\d{4})$/, "$1 **** **** $2");
}
return value; //
};
</script>
<style scoped>
/* 使按钮和状态信息的显示更为美观 */
.bg-green-100 {
background-color: #d1fad6;
}
.bg-red-100 {
background-color: #fcd0d0;
}
.bg-yellow-100 {
background-color: #fdf2b8;
}
.text-green-600 {
color: #16a34a;
}
.text-red-600 {
color: #dc2626;
}
.text-yellow-600 {
color: #e4b200;
}
</style>

View File

@ -0,0 +1,86 @@
<template>
<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-2">二要素验证结果</h3>
<div v-if="data.result === 0" class="flex items-center space-x-4 mb-6">
<div class="h-12 w-12 rounded-full flex flex-shrink-0 items-center justify-center bg-green-500">
<span class="text-white text-lg font-bold"></span>
</div>
<div>
<p class="text-lg font-medium text-gray-800">一致</p>
<p class="text-sm text-gray-500">姓名和身份证号匹配成功</p>
</div>
</div>
<div v-else class="flex items-center space-x-4 mb-6">
<div class="h-12 w-12 rounded-full flex items-center justify-center bg-red-500">
<span class="text-white text-lg font-bold"></span>
</div>
<div>
<p class="text-lg font-medium text-gray-800">不一致</p>
<p class="text-sm text-gray-500">姓名和身份证号不匹配</p>
</div>
</div>
<!-- 核验详情 -->
<!-- <h3 class="text-lg font-semibold text-gray-700 mb-2">附加信息</h3> -->
<div class="text-sm text-gray-600 space-y-2 ">
<p>
<span class="font-medium text-gray-800">地址</span>
{{ data.address }}
</p>
<p>
<span class="font-medium text-gray-800">生日</span>
{{ formatBirthday(data.birthday) }}
</p>
<p>
<span class="font-medium text-gray-800">性别</span>
{{ data.sex }}
</p>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
},
});
//
const formatBirthday = (birthday) => {
if (!birthday) return "未知";
const year = birthday.slice(0, 4);
const month = birthday.slice(4, 6);
const day = birthday.slice(6, 8);
return `${year}${month}${day}`;
};
//
const maskValue = (value, type) => {
if (type === "name") {
//
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "id_card") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
}
return value;
};
const maskedParams = {
name: maskValue(props.params.name, "name"),
id_card: maskValue(props.params.id_card, "id_card"),
};
</script>
<style scoped></style>

View File

@ -12,10 +12,10 @@ const { data } = props;
//
const statusMap = {
0: {
text: "未登记",
text: "未婚或尚未登记结婚",
bgClass: "bg-yellow-100",
textClass: "text-yellow-700",
description: "未登记婚姻信息",
description: "进行民政登记婚姻",
},
1: {
text: "已婚",
@ -51,25 +51,15 @@ const currentStatus =
<template>
<div class="card">
<div class="status-info mb-4 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>
</div>
<!-- <div class="additional-info mt-6 text-center">
<p class="text-xs text-gray-500">
<strong>数据更新时间</strong> {{ lastUpdated }}
</p>
<p class="text-xs text-gray-500">
本数据仅供参考如有疑问请联系相关部门
</p>
</div> -->
</div>
</template>

222
src/ui/CP_C_B332.vue Normal file
View File

@ -0,0 +1,222 @@
<template>
<div class="flex flex-col items-center card">
<div class="max-w-4xl w-full space-y-6">
<!-- 基本信息 -->
<div>
<LTitle title="基本信息" type="blue-green" class="mb-4"></LTitle>
<div class="grid grid-cols-1 gap-4">
<div class="flex justify-between">
<span class="font-medium text-gray-800">号牌种类:</span>
<span>{{ plateTypeMapping[data.plateType] }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">核定载客数:</span>
<span>{{ data.passengers }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">车牌号:</span>
<span>{{ data.plate }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">车架号:</span>
<span>{{ data.vin }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">使用性质:</span>
<span>{{ data.properties }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">车辆类型:</span>
<span>{{ data.vehicleType }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">品牌名称:</span>
<span>{{ data.brandName }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">车身颜色:</span>
<span>{{ filterDataNull(bodyColorMapping[data.bodyColor]) }}</span>
</div>
</div>
</div>
<!-- 一致性核验 -->
<div>
<LTitle title="一致性核验" type="blue-green" class="mb-4"></LTitle>
<div class="grid grid-cols-1 gap-4">
<div class="flex justify-between">
<span class="font-medium text-gray-800">车牌种类一致性:</span>
<span>{{ data.carType }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">车牌号一致性:</span>
<span>{{ data.carNumber }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">姓名一致性:</span>
<span>{{ data.name }}</span>
</div>
</div>
</div>
<!-- 发动机信息 -->
<div>
<LTitle title="发动机信息" type="blue-green" class="mb-4"></LTitle>
<div class="grid grid-cols-1 gap-4">
<div class="flex justify-between">
<span class="font-medium text-gray-800">发动机型号:</span>
<span>{{ data.engineModel }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">发动机号:</span>
<span>{{ data.engineNumber }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">排量:</span>
<span>{{ data.cc }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">燃料种类:</span>
<span>{{ data.fuelType }}</span>
</div>
</div>
</div>
<!-- 车辆状态 -->
<div>
<LTitle title="车辆状态" type="blue-green" class="mb-4"></LTitle>
<div class="grid grid-cols-1 gap-4">
<div class="flex justify-between">
<span class="font-medium text-gray-800">机动车状态:</span>
<span>{{ data.vehicleStatus }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">强制报废期止:</span>
<span>{{ filterDataNull(data.retirementDate) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">检验有效期止:</span>
<span>{{ filterDataNull(data.validityDayEnd) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">初次登记日期:</span>
<span>{{ filterDataNull(data.firstIssueDate) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">年检日期:</span>
<span>{{ filterDataNull(data.jianCheTime) }}</span>
</div>
</div>
</div>
<!-- 尺寸及重量 -->
<div>
<LTitle title="车辆尺寸及重量" type="blue-green" class="mb-4"></LTitle>
<div class="grid grid-cols-1 gap-4">
<div class="flex justify-between">
<span class="font-medium text-gray-800">总质量:</span>
<span>{{ filterDataNull(data.crossWeight) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">整备质量:</span>
<span>{{ filterDataNull(data.curbWeight) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">轴数:</span>
<span>{{ filterDataNull(data.shaft) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">轴距:</span>
<span>{{ filterDataNull(data.wheelBase) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">前轮距:</span>
<span>{{ filterDataNull(data.frontTread) }}</span>
</div>
<div class="flex justify-between">
<span class="font-medium text-gray-800">后轮距:</span>
<span>{{ filterDataNull(data.rearTread) }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
},
});
//
const plateTypeMapping = {
"02": "小型汽车",
"01": "大型汽车",
"03": "使馆汽车",
"04": "领馆汽车",
"05": "境外汽车",
"06": "外籍汽车",
"07": "普通摩托车",
"08": "轻便摩托车",
"09": "使馆摩托车",
"10": "领馆摩托车",
"11": "境外摩托车",
"12": "外籍摩托车",
"13": "低速车",
"14": "拖拉机",
"15": "挂车",
"16": "教练汽车",
"17": "教练摩托车",
"20": "临时入境汽车",
"21": "临时入境摩托车",
"22": "临时行驶车",
"23": "警用汽车",
"24": "警用摩托",
"51": "新能源大型汽车",
"52": "新能源小型汽车"
};
//
const bodyColorMapping = {
"A": "白",
"B": "灰",
"C": "黄",
"D": "粉",
"E": "红",
"F": "紫",
"G": "绿",
"H": "蓝",
"I": "棕",
"J": "黑",
"K": "香槟",
"L": "银",
"M": "橙",
"N": "金",
"Z": "其他"
};
//
const maskValue = (value, type) => {
if (type === "name") {
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "car_license") {
return value.slice(0, 2) + "*".repeat(value.length - 2) + value.slice(-2);
}
return value;
};
const filterDataNull = (data) => {
return data ? data : '-'
}
</script>
<style scoped></style>

View File

@ -0,0 +1,89 @@
<template>
<div class="flex items-center justify-center card">
<div class="max-w-md w-full p-2">
<!-- 核验结果 -->
<h3 class="text-xl font-semibold text-gray-700 mb-4">核验结果</h3>
<div v-if="result" class="flex items-center space-x-4">
<div
:class="['h-12 w-12 rounded-full flex flex-shrink-0 items-center justify-center', result.statusClass]">
<span class="text-white text-lg font-bold">{{ result.icon }}</span>
</div>
<div>
<p class="text-lg font-medium text-gray-800">{{ result.message }}</p>
<p class="text-sm text-gray-500">{{ result.description }}</p>
</div>
</div>
<div v-else class="text-center mb-6">
<p class="text-gray-500">无效的核验代码</p>
</div>
<!-- 三要素请求参数 -->
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
},
});
//
const codeMapping = {
1000: {
statusClass: "bg-green-500",
icon: "✔",
message: "一致",
description: "姓名、证件号与手机号码信息完全匹配。",
},
1003: {
statusClass: "bg-yellow-500",
icon: "⚠",
message: "不一致",
description: "姓名、证件号与手机号码信息不一致。",
},
1004: {
statusClass: "bg-red-500",
icon: "✘",
message: "姓名不正确",
description: "请检查姓名输入是否正确。",
},
1005: {
statusClass: "bg-red-500",
icon: "✘",
message: "证件号码不正确",
description: "请检查证件号码输入是否正确。",
},
};
const result = codeMapping[props.data.code];
//
const maskValue = (value, type) => {
if (type === "name") {
//
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "id_card") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "mobile") {
//
return value.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
}
return value;
};
const maskedParams = {
name: maskValue(props.params.name, "name"),
id_card: maskValue(props.params.id_card, "id_card"),
mobile: maskValue(props.params.mobile, "mobile"),
};
</script>
<style scoped></style>

View File

@ -0,0 +1,80 @@
<template>
<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>
<div v-if="data.code === '1000'" class="flex items-center space-x-4 mb-6">
<div class="h-12 w-12 rounded-full flex flex-shrink-0 items-center justify-center bg-green-500">
<span class="text-white text-lg font-bold"></span>
</div>
<div>
<p class="text-lg font-medium text-gray-800">一致</p>
<p class="text-sm text-gray-500">姓名和手机号匹配成功</p>
</div>
</div>
<div v-else class="flex items-center space-x-4 mb-6">
<div class="h-12 w-12 rounded-full flex items-center flex-shrink-0 justify-center bg-red-500">
<span class="text-white text-lg font-bold"></span>
</div>
<div>
<p class="text-lg font-medium text-gray-800">不一致</p>
<p class="text-sm text-gray-500">手机二要素验证失败请检查输入信息</p>
</div>
</div>
<!-- 核验详情 -->
<h3 class="text-lg font-semibold text-gray-700 mb-2">附加信息</h3>
<div class="text-sm text-gray-600 space-y-2">
<p>
<span class="font-medium text-gray-800">运营商类型</span>
{{ getPhoneType(data.data.phoneType) }}
</p>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
data: {
type: Object,
required: true,
},
params: {
type: Object,
required: true,
},
});
//
const getPhoneType = (type) => {
const phoneTypeMapping = {
CMCC: "中国移动",
CUCC: "中国联通",
CTCC: "中国电信",
};
return phoneTypeMapping[type] || "未知类型";
};
//
const maskValue = (value, type) => {
if (type === "name") {
//
return value.length > 1 ? value[0] + "*".repeat(value.length - 1) : "*";
} else if (type === "mobile") {
//
return value.replace(/^(\d{3})(?:\d+)(\d{4})$/, "$1****$2");
}
return value;
};
const maskedParams = {
name: maskValue(props.params.name, "name"),
mobile: maskValue(props.params.mobile, "mobile"),
};
</script>
<style scoped></style>

174
src/utils/chatCrypto.js Normal file
View File

@ -0,0 +1,174 @@
/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS = CryptoJS || function (u, p) {
var d = {}, l = d.lib = {}, s = function () { }, t = l.Base = { extend: function (a) { s.prototype = this; var c = new s; a && c.mixIn(a); c.hasOwnProperty("init") || (c.init = function () { c.$super.init.apply(this, arguments) }); c.init.prototype = c; c.$super = this; return c }, create: function () { var a = this.extend(); a.init.apply(a, arguments); return a }, init: function () { }, mixIn: function (a) { for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]); a.hasOwnProperty("toString") && (this.toString = a.toString) }, clone: function () { return this.init.prototype.extend(this) } },
r = l.WordArray = t.extend({
init: function (a, c) { a = this.words = a || []; this.sigBytes = c != p ? c : 4 * a.length }, toString: function (a) { return (a || v).stringify(this) }, concat: function (a) { var c = this.words, e = a.words, j = this.sigBytes; a = a.sigBytes; this.clamp(); if (j % 4) for (var k = 0; k < a; k++)c[j + k >>> 2] |= (e[k >>> 2] >>> 24 - 8 * (k % 4) & 255) << 24 - 8 * ((j + k) % 4); else if (65535 < e.length) for (k = 0; k < a; k += 4)c[j + k >>> 2] = e[k >>> 2]; else c.push.apply(c, e); this.sigBytes += a; return this }, clamp: function () {
var a = this.words, c = this.sigBytes; a[c >>> 2] &= 4294967295 <<
32 - 8 * (c % 4); a.length = u.ceil(c / 4)
}, clone: function () { var a = t.clone.call(this); a.words = this.words.slice(0); return a }, random: function (a) { for (var c = [], e = 0; e < a; e += 4)c.push(4294967296 * u.random() | 0); return new r.init(c, a) }
}), w = d.enc = {}, v = w.Hex = {
stringify: function (a) { var c = a.words; a = a.sigBytes; for (var e = [], j = 0; j < a; j++) { var k = c[j >>> 2] >>> 24 - 8 * (j % 4) & 255; e.push((k >>> 4).toString(16)); e.push((k & 15).toString(16)) } return e.join("") }, parse: function (a) {
for (var c = a.length, e = [], j = 0; j < c; j += 2)e[j >>> 3] |= parseInt(a.substr(j,
2), 16) << 24 - 4 * (j % 8); return new r.init(e, c / 2)
}
}, b = w.Latin1 = { stringify: function (a) { var c = a.words; a = a.sigBytes; for (var e = [], j = 0; j < a; j++)e.push(String.fromCharCode(c[j >>> 2] >>> 24 - 8 * (j % 4) & 255)); return e.join("") }, parse: function (a) { for (var c = a.length, e = [], j = 0; j < c; j++)e[j >>> 2] |= (a.charCodeAt(j) & 255) << 24 - 8 * (j % 4); return new r.init(e, c) } }, x = w.Utf8 = { stringify: function (a) { try { return decodeURIComponent(escape(b.stringify(a))) } catch (c) { throw Error("Malformed UTF-8 data"); } }, parse: function (a) { return b.parse(unescape(encodeURIComponent(a))) } },
q = l.BufferedBlockAlgorithm = t.extend({
reset: function () { this._data = new r.init; this._nDataBytes = 0 }, _append: function (a) { "string" == typeof a && (a = x.parse(a)); this._data.concat(a); this._nDataBytes += a.sigBytes }, _process: function (a) { var c = this._data, e = c.words, j = c.sigBytes, k = this.blockSize, b = j / (4 * k), b = a ? u.ceil(b) : u.max((b | 0) - this._minBufferSize, 0); a = b * k; j = u.min(4 * a, j); if (a) { for (var q = 0; q < a; q += k)this._doProcessBlock(e, q); q = e.splice(0, a); c.sigBytes -= j } return new r.init(q, j) }, clone: function () {
var a = t.clone.call(this);
a._data = this._data.clone(); return a
}, _minBufferSize: 0
}); l.Hasher = q.extend({
cfg: t.extend(), init: function (a) { this.cfg = this.cfg.extend(a); this.reset() }, reset: function () { q.reset.call(this); this._doReset() }, update: function (a) { this._append(a); this._process(); return this }, finalize: function (a) { a && this._append(a); return this._doFinalize() }, blockSize: 16, _createHelper: function (a) { return function (b, e) { return (new a.init(e)).finalize(b) } }, _createHmacHelper: function (a) {
return function (b, e) {
return (new n.HMAC.init(a,
e)).finalize(b)
}
}
}); var n = d.algo = {}; return d
}(Math);
(function () {
var u = CryptoJS, p = u.lib.WordArray; u.enc.Base64 = {
stringify: function (d) { var l = d.words, p = d.sigBytes, t = this._map; d.clamp(); d = []; for (var r = 0; r < p; r += 3)for (var w = (l[r >>> 2] >>> 24 - 8 * (r % 4) & 255) << 16 | (l[r + 1 >>> 2] >>> 24 - 8 * ((r + 1) % 4) & 255) << 8 | l[r + 2 >>> 2] >>> 24 - 8 * ((r + 2) % 4) & 255, v = 0; 4 > v && r + 0.75 * v < p; v++)d.push(t.charAt(w >>> 6 * (3 - v) & 63)); if (l = t.charAt(64)) for (; d.length % 4;)d.push(l); return d.join("") }, parse: function (d) {
var l = d.length, s = this._map, t = s.charAt(64); t && (t = d.indexOf(t), -1 != t && (l = t)); for (var t = [], r = 0, w = 0; w <
l; w++)if (w % 4) { var v = s.indexOf(d.charAt(w - 1)) << 2 * (w % 4), b = s.indexOf(d.charAt(w)) >>> 6 - 2 * (w % 4); t[r >>> 2] |= (v | b) << 24 - 8 * (r % 4); r++ } return p.create(t, r)
}, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
}
})();
(function (u) {
function p (b, n, a, c, e, j, k) { b = b + (n & a | ~n & c) + e + k; return (b << j | b >>> 32 - j) + n } function d (b, n, a, c, e, j, k) { b = b + (n & c | a & ~c) + e + k; return (b << j | b >>> 32 - j) + n } function l (b, n, a, c, e, j, k) { b = b + (n ^ a ^ c) + e + k; return (b << j | b >>> 32 - j) + n } function s (b, n, a, c, e, j, k) { b = b + (a ^ (n | ~c)) + e + k; return (b << j | b >>> 32 - j) + n } for (var t = CryptoJS, r = t.lib, w = r.WordArray, v = r.Hasher, r = t.algo, b = [], x = 0; 64 > x; x++)b[x] = 4294967296 * u.abs(u.sin(x + 1)) | 0; r = r.MD5 = v.extend({
_doReset: function () { this._hash = new w.init([1732584193, 4023233417, 2562383102, 271733878]) },
_doProcessBlock: function (q, n) {
for (var a = 0; 16 > a; a++) { var c = n + a, e = q[c]; q[c] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360 } var a = this._hash.words, c = q[n + 0], e = q[n + 1], j = q[n + 2], k = q[n + 3], z = q[n + 4], r = q[n + 5], t = q[n + 6], w = q[n + 7], v = q[n + 8], A = q[n + 9], B = q[n + 10], C = q[n + 11], u = q[n + 12], D = q[n + 13], E = q[n + 14], x = q[n + 15], f = a[0], m = a[1], g = a[2], h = a[3], f = p(f, m, g, h, c, 7, b[0]), h = p(h, f, m, g, e, 12, b[1]), g = p(g, h, f, m, j, 17, b[2]), m = p(m, g, h, f, k, 22, b[3]), f = p(f, m, g, h, z, 7, b[4]), h = p(h, f, m, g, r, 12, b[5]), g = p(g, h, f, m, t, 17, b[6]), m = p(m, g, h, f, w, 22, b[7]),
f = p(f, m, g, h, v, 7, b[8]), h = p(h, f, m, g, A, 12, b[9]), g = p(g, h, f, m, B, 17, b[10]), m = p(m, g, h, f, C, 22, b[11]), f = p(f, m, g, h, u, 7, b[12]), h = p(h, f, m, g, D, 12, b[13]), g = p(g, h, f, m, E, 17, b[14]), m = p(m, g, h, f, x, 22, b[15]), f = d(f, m, g, h, e, 5, b[16]), h = d(h, f, m, g, t, 9, b[17]), g = d(g, h, f, m, C, 14, b[18]), m = d(m, g, h, f, c, 20, b[19]), f = d(f, m, g, h, r, 5, b[20]), h = d(h, f, m, g, B, 9, b[21]), g = d(g, h, f, m, x, 14, b[22]), m = d(m, g, h, f, z, 20, b[23]), f = d(f, m, g, h, A, 5, b[24]), h = d(h, f, m, g, E, 9, b[25]), g = d(g, h, f, m, k, 14, b[26]), m = d(m, g, h, f, v, 20, b[27]), f = d(f, m, g, h, D, 5, b[28]), h = d(h, f,
m, g, j, 9, b[29]), g = d(g, h, f, m, w, 14, b[30]), m = d(m, g, h, f, u, 20, b[31]), f = l(f, m, g, h, r, 4, b[32]), h = l(h, f, m, g, v, 11, b[33]), g = l(g, h, f, m, C, 16, b[34]), m = l(m, g, h, f, E, 23, b[35]), f = l(f, m, g, h, e, 4, b[36]), h = l(h, f, m, g, z, 11, b[37]), g = l(g, h, f, m, w, 16, b[38]), m = l(m, g, h, f, B, 23, b[39]), f = l(f, m, g, h, D, 4, b[40]), h = l(h, f, m, g, c, 11, b[41]), g = l(g, h, f, m, k, 16, b[42]), m = l(m, g, h, f, t, 23, b[43]), f = l(f, m, g, h, A, 4, b[44]), h = l(h, f, m, g, u, 11, b[45]), g = l(g, h, f, m, x, 16, b[46]), m = l(m, g, h, f, j, 23, b[47]), f = s(f, m, g, h, c, 6, b[48]), h = s(h, f, m, g, w, 10, b[49]), g = s(g, h, f, m,
E, 15, b[50]), m = s(m, g, h, f, r, 21, b[51]), f = s(f, m, g, h, u, 6, b[52]), h = s(h, f, m, g, k, 10, b[53]), g = s(g, h, f, m, B, 15, b[54]), m = s(m, g, h, f, e, 21, b[55]), f = s(f, m, g, h, v, 6, b[56]), h = s(h, f, m, g, x, 10, b[57]), g = s(g, h, f, m, t, 15, b[58]), m = s(m, g, h, f, D, 21, b[59]), f = s(f, m, g, h, z, 6, b[60]), h = s(h, f, m, g, C, 10, b[61]), g = s(g, h, f, m, j, 15, b[62]), m = s(m, g, h, f, A, 21, b[63]); a[0] = a[0] + f | 0; a[1] = a[1] + m | 0; a[2] = a[2] + g | 0; a[3] = a[3] + h | 0
}, _doFinalize: function () {
var b = this._data, n = b.words, a = 8 * this._nDataBytes, c = 8 * b.sigBytes; n[c >>> 5] |= 128 << 24 - c % 32; var e = u.floor(a /
4294967296); n[(c + 64 >>> 9 << 4) + 15] = (e << 8 | e >>> 24) & 16711935 | (e << 24 | e >>> 8) & 4278255360; n[(c + 64 >>> 9 << 4) + 14] = (a << 8 | a >>> 24) & 16711935 | (a << 24 | a >>> 8) & 4278255360; b.sigBytes = 4 * (n.length + 1); this._process(); b = this._hash; n = b.words; for (a = 0; 4 > a; a++)c = n[a], n[a] = (c << 8 | c >>> 24) & 16711935 | (c << 24 | c >>> 8) & 4278255360; return b
}, clone: function () { var b = v.clone.call(this); b._hash = this._hash.clone(); return b }
}); t.MD5 = v._createHelper(r); t.HmacMD5 = v._createHmacHelper(r)
})(Math);
(function () {
var u = CryptoJS, p = u.lib, d = p.Base, l = p.WordArray, p = u.algo, s = p.EvpKDF = d.extend({ cfg: d.extend({ keySize: 4, hasher: p.MD5, iterations: 1 }), init: function (d) { this.cfg = this.cfg.extend(d) }, compute: function (d, r) { for (var p = this.cfg, s = p.hasher.create(), b = l.create(), u = b.words, q = p.keySize, p = p.iterations; u.length < q;) { n && s.update(n); var n = s.update(d).finalize(r); s.reset(); for (var a = 1; a < p; a++)n = s.finalize(n), s.reset(); b.concat(n) } b.sigBytes = 4 * q; return b } }); u.EvpKDF = function (d, l, p) {
return s.create(p).compute(d,
l)
}
})();
CryptoJS.lib.Cipher || function (u) {
var p = CryptoJS, d = p.lib, l = d.Base, s = d.WordArray, t = d.BufferedBlockAlgorithm, r = p.enc.Base64, w = p.algo.EvpKDF, v = d.Cipher = t.extend({
cfg: l.extend(), createEncryptor: function (e, a) { return this.create(this._ENC_XFORM_MODE, e, a) }, createDecryptor: function (e, a) { return this.create(this._DEC_XFORM_MODE, e, a) }, init: function (e, a, b) { this.cfg = this.cfg.extend(b); this._xformMode = e; this._key = a; this.reset() }, reset: function () { t.reset.call(this); this._doReset() }, process: function (e) { this._append(e); return this._process() },
finalize: function (e) { e && this._append(e); return this._doFinalize() }, keySize: 4, ivSize: 4, _ENC_XFORM_MODE: 1, _DEC_XFORM_MODE: 2, _createHelper: function (e) { return { encrypt: function (b, k, d) { return ("string" == typeof k ? c : a).encrypt(e, b, k, d) }, decrypt: function (b, k, d) { return ("string" == typeof k ? c : a).decrypt(e, b, k, d) } } }
}); d.StreamCipher = v.extend({ _doFinalize: function () { return this._process(!0) }, blockSize: 1 }); var b = p.mode = {}, x = function (e, a, b) {
var c = this._iv; c ? this._iv = u : c = this._prevBlock; for (var d = 0; d < b; d++)e[a + d] ^=
c[d]
}, q = (d.BlockCipherMode = l.extend({ createEncryptor: function (e, a) { return this.Encryptor.create(e, a) }, createDecryptor: function (e, a) { return this.Decryptor.create(e, a) }, init: function (e, a) { this._cipher = e; this._iv = a } })).extend(); q.Encryptor = q.extend({ processBlock: function (e, a) { var b = this._cipher, c = b.blockSize; x.call(this, e, a, c); b.encryptBlock(e, a); this._prevBlock = e.slice(a, a + c) } }); q.Decryptor = q.extend({
processBlock: function (e, a) {
var b = this._cipher, c = b.blockSize, d = e.slice(a, a + c); b.decryptBlock(e, a); x.call(this,
e, a, c); this._prevBlock = d
}
}); b = b.CBC = q; q = (p.pad = {}).Pkcs7 = { pad: function (a, b) { for (var c = 4 * b, c = c - a.sigBytes % c, d = c << 24 | c << 16 | c << 8 | c, l = [], n = 0; n < c; n += 4)l.push(d); c = s.create(l, c); a.concat(c) }, unpad: function (a) { a.sigBytes -= a.words[a.sigBytes - 1 >>> 2] & 255 } }; d.BlockCipher = v.extend({
cfg: v.cfg.extend({ mode: b, padding: q }), reset: function () {
v.reset.call(this); var a = this.cfg, b = a.iv, a = a.mode; if (this._xformMode == this._ENC_XFORM_MODE) var c = a.createEncryptor; else c = a.createDecryptor, this._minBufferSize = 1; this._mode = c.call(a,
this, b && b.words)
}, _doProcessBlock: function (a, b) { this._mode.processBlock(a, b) }, _doFinalize: function () { var a = this.cfg.padding; if (this._xformMode == this._ENC_XFORM_MODE) { a.pad(this._data, this.blockSize); var b = this._process(!0) } else b = this._process(!0), a.unpad(b); return b }, blockSize: 4
}); var n = d.CipherParams = l.extend({ init: function (a) { this.mixIn(a) }, toString: function (a) { return (a || this.formatter).stringify(this) } }), b = (p.format = {}).OpenSSL = {
stringify: function (a) {
var b = a.ciphertext; a = a.salt; return (a ? s.create([1398893684,
1701076831]).concat(a).concat(b) : b).toString(r)
}, parse: function (a) { a = r.parse(a); var b = a.words; if (1398893684 == b[0] && 1701076831 == b[1]) { var c = s.create(b.slice(2, 4)); b.splice(0, 4); a.sigBytes -= 16 } return n.create({ ciphertext: a, salt: c }) }
}, a = d.SerializableCipher = l.extend({
cfg: l.extend({ format: b }), encrypt: function (a, b, c, d) { d = this.cfg.extend(d); var l = a.createEncryptor(c, d); b = l.finalize(b); l = l.cfg; return n.create({ ciphertext: b, key: c, iv: l.iv, algorithm: a, mode: l.mode, padding: l.padding, blockSize: a.blockSize, formatter: d.format }) },
decrypt: function (a, b, c, d) { d = this.cfg.extend(d); b = this._parse(b, d.format); return a.createDecryptor(c, d).finalize(b.ciphertext) }, _parse: function (a, b) { return "string" == typeof a ? b.parse(a, this) : a }
}), p = (p.kdf = {}).OpenSSL = { execute: function (a, b, c, d) { d || (d = s.random(8)); a = w.create({ keySize: b + c }).compute(a, d); c = s.create(a.words.slice(b), 4 * c); a.sigBytes = 4 * b; return n.create({ key: a, iv: c, salt: d }) } }, c = d.PasswordBasedCipher = a.extend({
cfg: a.cfg.extend({ kdf: p }), encrypt: function (b, c, d, l) {
l = this.cfg.extend(l); d = l.kdf.execute(d,
b.keySize, b.ivSize); l.iv = d.iv; b = a.encrypt.call(this, b, c, d.key, l); b.mixIn(d); return b
}, decrypt: function (b, c, d, l) { l = this.cfg.extend(l); c = this._parse(c, l.format); d = l.kdf.execute(d, b.keySize, b.ivSize, c.salt); l.iv = d.iv; return a.decrypt.call(this, b, c, d.key, l) }
})
}();
(function () {
for (var u = CryptoJS, p = u.lib.BlockCipher, d = u.algo, l = [], s = [], t = [], r = [], w = [], v = [], b = [], x = [], q = [], n = [], a = [], c = 0; 256 > c; c++)a[c] = 128 > c ? c << 1 : c << 1 ^ 283; for (var e = 0, j = 0, c = 0; 256 > c; c++) { var k = j ^ j << 1 ^ j << 2 ^ j << 3 ^ j << 4, k = k >>> 8 ^ k & 255 ^ 99; l[e] = k; s[k] = e; var z = a[e], F = a[z], G = a[F], y = 257 * a[k] ^ 16843008 * k; t[e] = y << 24 | y >>> 8; r[e] = y << 16 | y >>> 16; w[e] = y << 8 | y >>> 24; v[e] = y; y = 16843009 * G ^ 65537 * F ^ 257 * z ^ 16843008 * e; b[k] = y << 24 | y >>> 8; x[k] = y << 16 | y >>> 16; q[k] = y << 8 | y >>> 24; n[k] = y; e ? (e = z ^ a[a[a[G ^ z]]], j ^= a[a[j]]) : e = j = 1 } var H = [0, 1, 2, 4, 8,
16, 32, 64, 128, 27, 54], d = d.AES = p.extend({
_doReset: function () {
for (var a = this._key, c = a.words, d = a.sigBytes / 4, a = 4 * ((this._nRounds = d + 6) + 1), e = this._keySchedule = [], j = 0; j < a; j++)if (j < d) e[j] = c[j]; else { var k = e[j - 1]; j % d ? 6 < d && 4 == j % d && (k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255]) : (k = k << 8 | k >>> 24, k = l[k >>> 24] << 24 | l[k >>> 16 & 255] << 16 | l[k >>> 8 & 255] << 8 | l[k & 255], k ^= H[j / d | 0] << 24); e[j] = e[j - d] ^ k } c = this._invKeySchedule = []; for (d = 0; d < a; d++)j = a - d, k = d % 4 ? e[j] : e[j - 4], c[d] = 4 > d || 4 >= j ? k : b[l[k >>> 24]] ^ x[l[k >>> 16 & 255]] ^ q[l[k >>>
8 & 255]] ^ n[l[k & 255]]
}, encryptBlock: function (a, b) { this._doCryptBlock(a, b, this._keySchedule, t, r, w, v, l) }, decryptBlock: function (a, c) { var d = a[c + 1]; a[c + 1] = a[c + 3]; a[c + 3] = d; this._doCryptBlock(a, c, this._invKeySchedule, b, x, q, n, s); d = a[c + 1]; a[c + 1] = a[c + 3]; a[c + 3] = d }, _doCryptBlock: function (a, b, c, d, e, j, l, f) {
for (var m = this._nRounds, g = a[b] ^ c[0], h = a[b + 1] ^ c[1], k = a[b + 2] ^ c[2], n = a[b + 3] ^ c[3], p = 4, r = 1; r < m; r++)var q = d[g >>> 24] ^ e[h >>> 16 & 255] ^ j[k >>> 8 & 255] ^ l[n & 255] ^ c[p++], s = d[h >>> 24] ^ e[k >>> 16 & 255] ^ j[n >>> 8 & 255] ^ l[g & 255] ^ c[p++], t =
d[k >>> 24] ^ e[n >>> 16 & 255] ^ j[g >>> 8 & 255] ^ l[h & 255] ^ c[p++], n = d[n >>> 24] ^ e[g >>> 16 & 255] ^ j[h >>> 8 & 255] ^ l[k & 255] ^ c[p++], g = q, h = s, k = t; q = (f[g >>> 24] << 24 | f[h >>> 16 & 255] << 16 | f[k >>> 8 & 255] << 8 | f[n & 255]) ^ c[p++]; s = (f[h >>> 24] << 24 | f[k >>> 16 & 255] << 16 | f[n >>> 8 & 255] << 8 | f[g & 255]) ^ c[p++]; t = (f[k >>> 24] << 24 | f[n >>> 16 & 255] << 16 | f[g >>> 8 & 255] << 8 | f[h & 255]) ^ c[p++]; n = (f[n >>> 24] << 24 | f[g >>> 16 & 255] << 16 | f[h >>> 8 & 255] << 8 | f[k & 255]) ^ c[p++]; a[b] = q; a[b + 1] = s; a[b + 2] = t; a[b + 3] = n
}, keySize: 8
}); u.AES = p._createHelper(d)
})();
CryptoJS.encrypt = function (word, key, iv) {
return encrypt(word, key, iv)
}
CryptoJS.decrypt = function (word, key, iv) {
return decrypt(word, key, iv)
}
/**
* 加密
* word原密码
* key key
* iv iv
*/
function encrypt (word, key, iv) {
key = CryptoJS.enc.Utf8.parse(key);
iv = CryptoJS.enc.Utf8.parse(iv);
var encrypted = CryptoJS.AES.encrypt(word, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
/**
* 解密
* word加密后的密码
* key key
* iv iv
*/
function decrypt (word, key, iv) {
key = CryptoJS.enc.Utf8.parse(key);
iv = CryptoJS.enc.Utf8.parse(iv);
var decrypted = CryptoJS.AES.decrypt(word, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
decrypted = CryptoJS.enc.Utf8.stringify(decrypted);
return decrypted;
}
/**
* Electronic Codebook block mode.
*/
CryptoJS.mode.ECB = (function () {
var ECB = CryptoJS.lib.BlockCipherMode.extend();
ECB.Encryptor = ECB.extend({
processBlock: function (words, offset) {
this._cipher.encryptBlock(words, offset);
}
});
ECB.Decryptor = ECB.extend({
processBlock: function (words, offset) {
this._cipher.decryptBlock(words, offset);
}
});
return ECB;
}());
/**
* @example
* var CryptoJS = require('./util/aes.js')
* var key = CryptoJS.enc.Utf8.parse("key");
* var iv = CryptoJS.enc.Utf8.parse("iv");
* var pwd = CryptoJS.encrypt(this.data.pwdVal, key, iv)
* var original = CryptoJS.encrypt(pwd, key, iv)
*/
export default CryptoJS;

19
src/utils/chatEncrypt.js Normal file
View File

@ -0,0 +1,19 @@
import Crypto from '@/utils/chatCrypto'
// 秘钥转换成utf8格式字符串用于加密解密一般长度是16位由后端提供
const key = Crypto.enc.Utf8.parse('qw5w6SFE2D1jmxyd')
// 偏移量转换成utf8格式字符串一般长度是16位(由后端提供)
const iv = Crypto.enc.Utf8.parse('345GDFED433223DF')
// 加密使用CBC模式
export default function Encrypt(value) {
// 使用外部包中的AES的加密方法
// value(加密内容)、key(密钥)
let encrypt = Crypto.AES.encrypt(value, key, {
iv, // 偏移量
mode: Crypto.mode.CBC, // 模式(五种加密模式)
padding: Crypto.pad.Pkcs7 // 填充
})
// 将加密的内容转成字符串返回出去
return encrypt.toString()
}

195
src/views/Ai.vue Normal file
View File

@ -0,0 +1,195 @@
<template>
<div class="flex flex-col box-border from-blue-100 to-white bg-gradient-to-b pt-4 flex-1 pb-4">
<div class="mx-4 flex flex-col flex-1 rounded-xl shadow-lg">
<!-- Chat Window -->
<div class="w-full text-center py-2 text-slate-800 font-bold text-xl bg-white rounded-t-xl">AI律师</div>
<div class="flex flex-col flex-1 p-4">
<div class=" flex-1 overflow-y-auto">
<div v-for="(message, index) in messages" :key="index" class="mb-4">
<div v-if="message.sender === 'ai'" class="flex justify-start items-start">
<img class="w-10 h-10 rounded-xl mr-2" src="@/assets/images/ai_picture.webp" alt="AI律师">
<div
class="inline-block max-w-max rounded-xl bg-white p-2 text-left text-green-600 font-medium shadow-md">
<!-- If AI message, show loading or text -->
<div v-if="message.loading" class="flex justify-center items-center">
<div class="loader"></div> <!-- Loading Spinner -->
</div>
<div v-else>
{{ message.text }}
</div>
</div>
</div>
<div v-else class="flex justify-end">
<div class=" ml-auto inline-block max-w-max rounded-xl from-sky-300 via-sky-300 to-sky-300
bg-gradient-to-r p-2 text-right text-white font-medium shadow-md">
{{ message.text }}
</div>
</div>
</div>
</div>
<!-- Input Area -->
<div class="p-2 w-full input-area flex items-center gap-2">
<input v-model="userMessage" placeholder="请输入您的问题..." class="flex-1 p-2 border rounded-lg"
type="text" />
<button class="shadow p-2 bg-blue-500 text-white rounded-lg" @click="sendMessage">
发送
</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import chatEncrypt from "@/utils/chatEncrypt"
const userMessage = ref('') //
const messages = ref([ // AI
{ sender: 'ai', text: '欢迎!请问有什么可以帮助您的吗?' },
])
const sessionID = ref("") // sessionID
let reader;
async function sendMessage() {
if (userMessage.value.trim() === '') return;
//
messages.value.push({ sender: 'user', text: userMessage.value });
const t = Date.now();
const x = chatEncrypt(String(t));
// AI
const aiMessage = { sender: 'ai', text: '', loading: true };
messages.value.push(aiMessage);
//
fetch('/api/v1/chat/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
x,
t,
},
body: JSON.stringify({
prompt: userMessage.value,
platform_id: 2,
roleid: 1,
openid: 'openid' + localStorage.getItem("token"),
userid: 'userid' + localStorage.getItem("token"),
sessionid: sessionID.value // sessionID
}),
})
.then((response) => {
if (response.ok && response.body) {
// ReadableStream
reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
// 使
const readStream = () => {
reader.read().then(({ done, value }) => {
if (done) {
return;
}
buffer += decoder.decode(value, { stream: true });
//
let newLineIndex;
while ((newLineIndex = buffer.indexOf('\n')) !== -1) {
let message = buffer.slice(0, newLineIndex).trim();
if (message) {
aiMessage.loading = false; //
if (message.startsWith('data:')) {
message = message.replace('data:', '').trim(); // 'message data:'
}
try {
//
const parsedMessage = JSON.parse(message);
console.log("parsedMessage", parsedMessage)
// session_id
sessionID.value = parsedMessage.output.session_id;
// AI
const aiText = parsedMessage.output.text;
// AI
aiMessage.text += aiText;
messages.value = [...messages.value]; //
} catch (e) {
console.error('Failed to parse message:', e);
}
}
buffer = buffer.slice(newLineIndex + 1);
}
//
readStream();
});
};
readStream();
} else {
console.error('Failed to fetch stream');
}
})
.catch((error) => {
console.error('Request failed:', error);
});
userMessage.value = ''; //
}
onBeforeUnmount(() => {
if (reader) {
reader.cancel(); //
}
});
</script>
<style scoped>
input {
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
}
button {
padding: 0.5rem 1rem;
background-color: #007bff;
border: none;
border-radius: 0.5rem;
color: white;
}
button:hover {
background-color: #0056b3;
}
.loader {
border: 4px solid #f3f3f3;
/* Light gray */
border-top: 4px solid #3498db;
/* Blue */
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -1,20 +1,10 @@
<template>
<div class="min-h-screen bg-gray-100 flex flex-col p-4">
<!-- 标题 -->
<div class="text-center text-2xl font-bold mb-4">授权书</div>
<!-- 授权书滚动区域 -->
<div
class="authrization-card card flex-1 overflow-y-auto"
ref="agreementBox"
@scroll="handleScroll"
>
<div class=" card flex-1 overflow-y-auto" ref="agreementBox" @scroll="handleScroll">
<p class="my-2">海南省学宇思网络科技有限公司</p>
<p class="indent-[2em]">
本人<span class="font-bold">
{{ signature ? userData.name : "____________" }}</span
>
拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
<!-- <span class="font-bold"> {{ signature ? userData.name : "____________" }}</span> -->
本人拟向贵司申请大数据分析报告查询业务贵司需要了解本人相关状况用于查询大数据分析报告因此本人同意向贵司提供本人的姓名和手机号等个人信息并同意贵司向第三方包括但不限于西部数据交易有限公司传送上述信息第三方将使用上述信息核实信息真实情况查询信用记录并生成报告
</p>
<p class="mt-2 font-bold">授权内容如下</p>
<ol class="list-decimal pl-6">
@ -69,7 +59,7 @@
本人有权随时撤回本授权书中的授权但撤回前的授权行为及其法律后果仍具有法律效力若需撤回授权本人可通过贵司官方渠道提交书面申请贵司将在收到申请后依法停止对本人数据的使用
</li>
<li>
你通过全能查APP或推广方推广查询模式自愿支付相应费用用于购买海南省学宇思网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
你通过全能查自愿支付相应费用用于购买海南省学宇思网络科技有限公司的大数据报告产品如若对产品内容存在异议可通过邮箱admin@iieeii.com或APP联系客服按钮进行反馈贵司将在收到异议之日起20日内进行核查和处理并将结果答复
</li>
<li>
你向海南省学宇思网络科技有限公司的支付方式为海南省学宇思网络科技有限公司及其经官方授权的相关企业的支付宝账户
@ -88,47 +78,13 @@
</li>
</ul>
<p class="mt-2">本授权书于 {{ signTime }}生效</p>
<p class="mt-4 font-bold">
签署人<span class="underline">{{
signature ? userData.name : "____________"
}}</span>
<br />
手机号码<span class="underline">
{{ signature ? userData.phone : "____________" }}
</span>
<br />
签署时间<span class="underline">{{ signTime }}</span>
</p>
</div>
<!-- 操作按钮 -->
<div class="mt-4 flex justify-between">
<button
class="flex-shrink-0 bg-red-500 text-white px-4 py-2 rounded-lg"
@click="cancel"
>
取消
</button>
<div class="mt-2 px-2 text-center text-sm text-gray-500">
{{ scrollMessage }}
</div>
<button
class="flex-shrink-0 bg-blue-500 text-white px-4 py-2 rounded-lg active:bg-blue-600"
:class="
!canAgree &&
'bg-gray-300 cursor-not-allowed active:bg-gray-300'
"
:disabled="!canAgree"
@click="agree"
>
{{ signature ? "同意" : "签署" }}
</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useFetch } from "@vueuse/core";
import { useWebView } from "@/composables/useWebView";
const { postMessage } = useWebView();
const userData = ref({
name: "",
idCard: "",
@ -207,7 +163,7 @@ const agree = () => {
signature.value = true;
return;
}
uni.postMessage({
postMessage({
data: {
action: "agreed",
},
@ -216,7 +172,7 @@ const agree = () => {
//
const cancel = () => {
uni.postMessage({
postMessage({
data: {
action: "cancelled",
},
@ -231,7 +187,7 @@ onUnmounted(() => {
document.removeEventListener("UniAppJSBridgeReady", handleBridgeReady);
});
const handleBridgeReady = () => {
uni.postMessage({
postMessage({
data: {
loaded: true,
},
@ -239,8 +195,4 @@ const handleBridgeReady = () => {
};
</script>
<style scoped>
.authrization-card {
max-height: calc(100vh - 200px);
}
</style>
<style scoped></style>

261
src/views/Example copy.vue Normal file
View File

@ -0,0 +1,261 @@
<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>

View File

@ -1,244 +1,280 @@
<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: "个人风险评估",
const featureMap = {
G09SC02: {
name: '单人婚姻',
component: defineAsyncComponent(() => import('@/ui/CMarriage.vue')),
},
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')),
},
G05HZ01: {
name: '股东人企关系',
component: defineAsyncComponent(() => import('@/ui/CRelatedEnterprises.vue')),
},
Q23SC01: {
name: '企业涉诉',
component: defineAsyncComponent(() => import('@/ui/CLawsuit.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')),
}
};
// product_id
function getProductName(productId) {
return productMap[productId] || "未知类型";
}
import LEmpty from "@/components/LEmpty.vue";
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);
});
const reportData = ref([])
const reportParams = ref({})
const reportName = ref("")
const reportDateTime = ref(null)
const feature = ref("")
const isEmpty = ref(false)
onMounted(() => {
const query = new URLSearchParams(window.location.search);
feature.value = query.get("feature");
token.value = query.get("token") || "";
console.log("feature", feature.value)
if (!feature.value) return;
getReport()
});
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,
};
},
}
)
const getReport = async () => {
let queryUrl = `/query/example?feature=${feature.value}`
const { data, error } = await useApiFetch(queryUrl)
.get()
.json();
.json()
onFetchResponse(() => {
console.log("data", data.value);
if (data.value && !error.value) {
if (data.value.code === 200) {
if (data.value.data.product_name === '婚姻评估') {
reportData.value = data.value.data.query_data.reverse()
} else {
reportData.value = data.value.data.query_data
}
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}`);
reportParams.value = data.value.data.query_params
reportName.value = data.value.data.product_name
reportDateTime.value = data.value.data.create_time
} else if (data.value.code === 200003) {
isEmpty.value = true
}
}
});
}
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") {
// 64
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") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "ent_name") {
// 33 "*"
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); // 633
}
} else if (type === "ent_code") {
// 44 "*"
if (value.length <= 8) {
return value.slice(0, 4) + "*".repeat(value.length - 4); // 84 *
} else {
return value.slice(0, 4) + "*".repeat(value.length - 8) + value.slice(-4); // 844
}
} else if (type === "car_license") {
// 22 "*"
if (value.length <= 4) {
return value[0] + "*".repeat(value.length - 1); // 4
} else {
// 22 "*"
return value.slice(0, 2) + "*".repeat(value.length - 4) + value.slice(-2);
}
}
return value;
}
});
});
</script>
<template>
<div class="min-h-full from-blue-100 to-white bg-gradient-to-b">
<van-notice-bar color="#e03131" background="#ecf9ff" left-icon="info-o" text="由于全能查APP暂未上线建议将此网站加入收藏夹或书签" />
<!-- <CTabs :tabs="sortedTabs" type="blue-green" /> -->
<template v-if="isDone">
<div class="flex flex-col gap-y-4 p-4 pt-12">
<div class="flex flex-col gap-y-4 p-4">
<div id="overdiv" class="title">报告概述</div>
<div class="card">
<div class="flex flex-col gap-y-2">
<div class="flex justify-between">
<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">2024年11月18日 23:11:23</span>
<span class="text-gray-600">2025-1-1 12:00:00</span>
</div>
<div class="flex justify-between">
<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">{{
getProductName(productId)
<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>
<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>
</template>
</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" />
<LEmpty v-if="isEmpty" />
<template v-for="(item, index) in reportData" :key="index">
<div id="lawsuit" class="title">{{ featureMap[item.apiID].name }}</div>
<component :is="featureMap[item.apiID].component" :data="item.data" :params="reportParams">
</component>
</template>
<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">30</strong>过期自动删除
@ -247,15 +283,46 @@ onMounted(() => {
&nbsp; &nbsp;
若您的数据不全面可能是数据具有延迟性或者合作信息机构未获取到您的数据若数据有错误请联系客服
</p>
<p> &nbsp;
&nbsp;本产品所有数据均来自第三方可能部分数据未公开数据更新延迟或信息受到限制贵司不对数据的准确性真实性完整性做任何承诺用户需根据实际情况结合报告内容自行判断与决策
</p>
</div>
</div>
</div>
</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;
}
.disclaimer {
/* margin-top: 24px; */
padding: 10px;
font-size: 12px;
color: #999;
text-align: center;
border-top: 1px solid #e0e0e0;
padding-bottom: 60px;
background: #ffffff;
}
</style>

130
src/views/HistoryQuery.vue Normal file
View File

@ -0,0 +1,130 @@
<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)
//
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}`)
.get()
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
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
}
if (reportList.value.length >= total.value) {
finished.value = true
}
}
}
loading.value = false
}
//
onMounted(() => {
fetchData()
})
//
const onLoad = () => {
if (!finished.value) {
console.log("finished", finished.value)
if (num.value >= max.value) {
finished.value = true
} else {
fetchData()
}
}
}
function toDetail(item) {
router.push({ path: '/report', query: { orderId: item.order_id } });
}
//
function stateText(state) {
switch (state) {
case 'pending':
return '查询中'
case 'success':
return '查询成功'
case 'failed':
return '查询失败'
default:
return '未知状态'
}
}
//
function statusClass(state) {
switch (state) {
case 'pending':
return 'status-pending'
case 'success':
return 'status-success'
case 'failed':
return 'status-failed'
default:
return ''
}
}
</script>
<template>
<!-- Vant 通知栏 -->
<van-notice-bar color="#1989fa" background="#ecf9ff" left-icon="info-o"
text="为保证用户的隐私以及数据安全您的报告生成30天之后将自动清除请及时保存您的报告。" />
<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">
<div class="flex items-center justify-between">
<div>状态:</div>
<div class="rounded-xl px-2 py-1" :class="[statusClass(item.query_state)]">
{{ stateText(item.query_state) }}
</div>
</div>
<div class="flex items-center justify-between">
<div>报告类型</div>
<div>{{ item.product_name }}</div>
</div>
<div class="flex items-center justify-between">
<div>查询时间:</div>
<div>{{ item.create_time }}</div>
</div>
</div>
</van-cell>
</van-list>
</div>
</template>
<style scoped>
.status-pending {
background-color: #fff3cd;
color: #856404;
}
.status-success {
background-color: #d4edda;
color: #155724;
}
.status-failed {
background-color: #f8d7da;
color: #721c24;
}
</style>

View File

@ -1,42 +1,30 @@
<template>
<div class="font-sans text-gray-800">
<!-- 头部导航 -->
<header
class="flex justify-between items-center bg-blue-100 p-4 border-b border-blue-200"
>
<header class="flex justify-between items-center bg-blue-100 p-4 border-b border-blue-200">
<div class="text-2xl font-bold text-blue-700">全能查 APP</div>
<button
class="lg:hidden text-blue-700 text-2xl focus:outline-none transition-transform transform hover:scale-110"
@click="toggleMenu"
>
@click="toggleMenu">
<span v-if="!isMenuOpen"></span>
<span v-else></span>
</button>
<nav
:class="`lg:flex lg:items-center lg:gap-6 ${
isMenuOpen
<nav :class="`lg:flex lg:items-center lg:gap-6 ${isMenuOpen
? 'flex flex-col items-end absolute top-16 right-0 bg-blue-100 w-full shadow-md py-4 px-6'
: 'hidden'
}`"
>
<a
href="#support"
class="text-blue-700 hover:text-blue-500 transition-colors"
@click="toggleMenu"
>联系我们</a
>
}`">
<a href="#support" class="text-blue-700 hover:text-blue-500 transition-colors"
@click="toggleMenu">联系我们</a>
</nav>
</header>
<!-- 横幅 -->
<section
class="text-center py-16 bg-gradient-to-r from-blue-100 to-blue-200 text-blue-800"
>
<section class="text-center py-16 bg-gradient-to-r from-blue-100 to-blue-200 text-blue-800">
<h1 class="text-4xl lg:text-5xl font-bold mb-4 animate-fade-in">
防范风险从全能查开始
</h1>
<p class="text-lg lg:text-xl mb-6 animate-fade-in delay-200">
全能查 APP为您的安全保驾护航
全能查为您的安全保驾护航
</p>
</section>
@ -44,21 +32,12 @@
<section id="features" class="py-16 bg-blue-50 text-center">
<h2 class="text-3xl font-bold text-blue-700 mb-6">APP 预览</h2>
<div class="flex flex-wrap justify-center gap-6">
<img
class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_1.jpg"
alt="APP preview"
/>
<img
class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_2.jpg"
alt="APP preview"
/>
<img
class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_3.jpg"
alt="APP preview"
/>
<img class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_1.jpg" alt="APP preview" />
<img class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_2.jpg" alt="APP preview" />
<img class="shadow-lg rounded-lg transition-transform transform hover:scale-105 h-96 object-contain"
src="/image/app_3.jpg" alt="APP preview" />
</div>
</section>
@ -68,9 +47,7 @@
<p class="text-lg text-blue-800 mb-2">
如需帮助请通过以下邮箱联系我们
</p>
<p
class="text-blue-700 underline hover:text-blue-500 transition-colors"
>
<p class="text-blue-700 underline hover:text-blue-500 transition-colors">
<a href="mailto:admin@iieeii.com">admin@iieeii.com</a>
</p>
</section>
@ -78,11 +55,8 @@
<!-- 页脚 -->
<footer class="bg-blue-200 text-gray-700 text-center py-4">
<div class="flex items-center justify-center gap-2">
<img
src="https://qcloudimg.tencent-cloud.cn/raw/eed02831a0e201b8d794c8282c40cf2e.png"
alt="网安 icon"
class="w-6 h-6"
/>
<img src="https://qcloudimg.tencent-cloud.cn/raw/eed02831a0e201b8d794c8282c40cf2e.png" alt="网安 icon"
class="w-6 h-6" />
<p class="text-sm">
琼公网安备 46010002000443 | 琼ICP备 2024038584 -2
</p>
@ -107,17 +81,21 @@ function toggleMenu() {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.animate-fade-in {
animation: fade-in 1s ease-out;
}
.animate-fade-in.delay-200 {
animation-delay: 0.2s;
}
.animate-fade-in.delay-400 {
animation-delay: 0.4s;
}

View File

@ -1,21 +1,34 @@
<script setup>
import { ref, reactive, computed, onMounted, onUnmounted } from "vue";
import { aesEncrypt } from '@/utils/crypto'
import { aesEncrypt } from "@/utils/crypto";
import { useRoute } from "vue-router";
import { useWebView } from "@/composables/useWebView";
import Authorization from "@/components/Authorization.vue";
import Payment from "@/components/Payment.vue";
import CarNumberInput from "@/components/CarNumberInput.vue";
const route = useRoute();
useWebView()
const showAuthorizationPopup = ref(false)
const authorization = ref(false)
const showPayment = ref(false)
const queryId = ref(null)
const router = useRouter()
const showAuthorizationPopup = ref(false);
const authorization = ref(true);
const showPayment = ref(false);
const queryId = ref(null);
const name = ref("");
const nameMan = ref("");
const nameWoman = ref("");
const idCard = ref("");
const idCardMan = ref("");
const idCardWoman = ref("");
const mobile = ref("");
const bankCard = ref("");
const startDate = ref([])
const dateVal = ref("")
const showDatePicker = ref(false)
//
const today = new Date();
const maxDate = today; //
// 200011
const minDate = new Date('2000-01-01');
const entName = ref("");
const entCode = ref("");
const verificationCode = ref("");
@ -24,11 +37,102 @@ const isCountingDown = ref(false);
const countdown = ref(60);
const feature = ref(route.params.feature);
const featureData = ref({});
const carLicense = ref("");
const carType = ref("小型汽车");
const carPickerVal = ref([{ value: "02", text: "小型汽车" }]);
const showCarTypePicker = ref(false);
const carTypeColumns = [
{ value: "01", text: "大型汽车" },
{ value: "02", text: "小型汽车" },
{ value: "03", text: "使馆汽车" },
{ value: "04", text: "领馆汽车" },
{ value: "05", text: "境外汽车" },
{ value: "06", text: "外籍汽车" },
{ value: "07", text: "普通摩托车" },
{ value: "08", text: "轻便摩托车" },
{ value: "09", text: "使馆摩托车" },
{ value: "10", text: "领馆摩托车" },
{ value: "11", text: "境外摩托车" },
{ value: "12", text: "外籍摩托车" },
{ value: "13", text: "低速车" },
{ value: "14", text: "拖拉机" },
{ value: "15", text: "挂车" },
{ value: "16", text: "教练汽车" },
{ value: "17", text: "教练摩托车" },
{ value: "20", text: "临时入境汽车" },
{ value: "21", text: "临时入境摩托车" },
{ value: "22", text: "临时行驶车" },
{ value: "23", text: "警用汽车" },
{ value: "24", text: "警用摩托车" },
{ value: "51", text: "新能源大型车" },
{ value: "52", text: "新能源小型车" },
];
const formatterDate = (type, option) => {
if (type === 'year') {
option.text += '年';
}
if (type === 'month') {
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
}
const carLicenseChange = (e) => {
console.log("carLicenseChange", e);
carLicense.value = e;
};
const onConfirmCarType = ({ selectedValues, selectedOptions }) => {
console.log(
"selectedValues, selectedOptions",
selectedValues,
selectedOptions
);
showCarTypePicker.value = false;
carPickerVal.value = selectedValues;
carType.value = selectedOptions[0].text;
};
onMounted(() => {
getProduct()
initAuthorization()
// discountPriceInit()
isFinishPayment()
getProduct();
initAuthorization();
});
const discountPrice = ref(false) //
// const discountPriceInit = () => {
// let m = localStorage.getItem("m")
// let hour = "12"
// if (m === "shifenliangzai") {
// hour = "00"
// }
// const currentDate = new Date()
// const startDate = new Date(`2025-01-01T${hour}:00:00+08:00`) // 20251112
// const endDate = new Date('2025-01-02T12:00:00+08:00') // 20251212
// console.log(startDate, endDate)
// if (currentDate >= startDate && currentDate <= endDate) {
// discountPrice.value = true //
// } else {
// discountPrice.value = false //
// }
// }
function isFinishPayment() {
const query = new URLSearchParams(window.location.search);
let orderNo = query.get("out_trade_no");
if (orderNo) {
router.push({ path: '/report', query: { orderNo } });
}
}
async function getProduct() {
const { data, error } = await useApiFetch(`/product/en/${feature.value}`)
.get()
@ -39,33 +143,127 @@ async function getProduct() {
}
}
function initAuthorization() {
if (noAuthorization.includes(feature.value)) {
authorization.value = true
if (NeedAuthorization.includes(feature.value)) {
authorization.value = false;
}
}
const isPhoneNumberValid = computed(() => {
return /^1[3-9]\d{9}$/.test(mobile.value);
});
const isIdCardValid = computed(() => /^\d{17}[\dX]$/i.test(idCard.value));
const isIdCardManValid = computed(() => /^\d{17}[\dX]$/i.test(idCardMan.value));
const isIdCardWomanValid = computed(() =>
/^\d{17}[\dX]$/i.test(idCardWoman.value)
);
const isCreditCodeValid = computed(() => /^.{18}$/.test(entCode.value));
const isCarLicense = computed(() => carLicense.value.trim().length > 6);
const isBankCardValid = computed(() => {
const card = bankCard.value.replace(/\D/g, ""); //
if (card.length < 13 || card.length > 19) {
return false; //
}
let sum = 0;
let shouldDouble = false;
//
for (let i = card.length - 1; i >= 0; i--) {
let digit = parseInt(card.charAt(i));
if (shouldDouble) {
digit *= 2;
if (digit > 9) {
digit -= 9;
}
}
sum += digit;
shouldDouble = !shouldDouble; // 2
}
return sum % 10 === 0; // 10
});
function handleSubmit() {
if (!agreeToTerms.value) {
showToast({ message: "请阅读并同意用户协议和隐私政策" });
showToast({ message: `请阅读并同意用户协议、隐私政策${!NeedAuthorization.includes(feature.value) ? '和授权书' : ''}` });
return;
}
if (
!validateField('name', name.value, v => v, '请输入姓名') ||
!validateField('mobile', mobile.value, v => isPhoneNumberValid.value, '请输入有效的手机号') ||
!validateField('idCard', idCard.value, v => isIdCardValid.value, '请输入有效的身份证号码') ||
!validateField('verificationCode', verificationCode.value, v => v, '请输入验证码') ||
!validateField('entName', entName.value, v => v, '请输入企业名称') ||
!validateField('entCode', entCode.value, v => isCreditCodeValid.value, '请输入统一社会信用代码')
!validateField("name", name.value, (v) => v, "请输入姓名") ||
!validateField("nameMan", nameMan.value, (v) => v, "请输入男方姓名") ||
!validateField(
"nameWoman",
nameWoman.value,
(v) => v,
"请输入女方姓名"
) ||
!validateField(
"mobile",
mobile.value,
(v) => isPhoneNumberValid.value,
"请输入有效的手机号"
) ||
!validateField(
"idCard",
idCard.value,
(v) => isIdCardValid.value,
"请输入有效的身份证号码"
) ||
!validateField(
"idCardMan",
idCardMan.value,
(v) => isIdCardManValid.value,
"请输入有效的男方身份证号码"
) ||
!validateField(
"idCardWoman",
idCardWoman.value,
(v) => isIdCardWomanValid.value,
"请输入有效的女方身份证号码"
) ||
!validateField(
"bankCard",
bankCard.value,
(v) => isBankCardValid.value,
"请输入有效的银行卡号码"
) ||
!validateField(
"verificationCode",
verificationCode.value,
(v) => v,
"请输入验证码"
) ||
!validateField(
"carPickerVal",
carPickerVal.value,
(v) => v,
"请选择车辆类型"
) ||
!validateField(
"carLicense",
carLicense.value,
(v) => isCarLicense.value,
"请输入正确的车牌号"
) ||
!validateField("entName", entName.value, (v) => v, "请输入企业名称") ||
!validateField(
"entCode",
entCode.value,
(v) => isCreditCodeValid.value,
"请输入统一社会信用代码"
) ||
!validateField(
"date",
dateVal.value,
(v) => v,
"请选择日期"
)
) {
return;
}
submitRequest()
submitRequest();
}
const validateField = (field, value, validationFn, errorMessage) => {
if (isHasInput(field) && !validationFn(value)) {
@ -73,59 +271,95 @@ const validateField = (field, value, validationFn, errorMessage) => {
return false;
}
return true;
}
};
const defaultInput = ["name", "mobile", "idCard", "verificationCode"]
const defaultInput = ["name", "idCard", "mobile", "verificationCode"];
const specialProduct = {
"toc_EnterpriseLawsuit": ["entName", "entCode", "mobile", "verificationCode"]
}
const noAuthorization = ["toc_EnterpriseLawsuit"]
toc_EnterpriseLawsuit: ["entName", "entCode", "mobile", "verificationCode"],
toc_PhoneThreeElements: ["name", "idCard", "mobile"],
toc_IDCardTwoElements: ["name", "idCard"],
toc_PhoneTwoElements: ["name", "mobile"],
toc_PersonVehicleVerification: ["name", "carType", "carLicense"],
toc_VehiclesUnderName: ["name", "idCard"],
toc_DualMarriage: ["nameMan", "idCardMan", "nameWoman", "idCardWoman"],
toc_BankCardBlacklist: ["name", "idCard", "mobile", "bankCard"],
toc_BankCardFourElements: ["name", "idCard", "mobile", "bankCard"],
toc_NaturalLifeStatus: ["name", "idCard"],
toc_NetworkDuration: ["mobile"],
toc_PhoneSecondaryCard: ["mobile", "date"],
toc_PhoneNumberRisk: ["mobile"],
};
const NeedAuthorization = [
"toc_Marriage"
];
const isHasInput = (input) => {
if (specialProduct[feature.value]) {
return specialProduct[feature.value].includes(input)
return specialProduct[feature.value].includes(input);
} else {
return defaultInput.includes(input)
return defaultInput.includes(input);
}
}
};
async function submitRequest() {
const req = {}
if (isHasInput('name')) {
req.name = name.value
const req = {};
if (isHasInput("name")) {
req.name = name.value;
}
if (isHasInput('id_card')) {
req.id_card = idCard.value
if (isHasInput("idCard")) {
req.id_card = idCard.value;
}
if (isHasInput('mobile')) {
req.mobile = mobile.value
if (isHasInput("nameMan")) {
req.name_man = nameMan.value;
}
if (isHasInput('verificationCode')) {
req.code = verificationCode.value
if (isHasInput("idCardMan")) {
req.id_card_man = idCardMan.value;
}
if (isHasInput('entName')) {
req.ent_name = entName.value
if (isHasInput("nameWoman")) {
req.name_woman = nameWoman.value;
}
if (isHasInput('entCode')) {
req.ent_code = entCode.value
if (isHasInput("idCardWoman")) {
req.id_card_woman = idCardWoman.value;
}
const reqStr = JSON.stringify(req)
const encodeData = aesEncrypt(reqStr, 'ff83609b2b24fc73196aac3d3dfb874f')
if (isHasInput("bankCard")) {
req.bank_card = bankCard.value.replace(/\D/g, "");
}
if (isHasInput("mobile")) {
req.mobile = mobile.value;
}
if (isHasInput("verificationCode")) {
req.code = verificationCode.value;
}
if (isHasInput("carType")) {
req.car_type = carPickerVal.value[0].value;
}
if (isHasInput("carLicense")) {
req.car_license = carLicense.value.trim();
}
if (isHasInput("date")) {
req.start_date = startDate.value.map(item => item).join('')
}
if (isHasInput("entName")) {
req.ent_name = entName.value;
}
if (isHasInput("entCode")) {
req.ent_code = entCode.value;
}
const reqStr = JSON.stringify(req);
const encodeData = aesEncrypt(reqStr, "ff83609b2b24fc73196aac3d3dfb874f");
const { data, error } = await useApiFetch(`/query/service/${feature.value}`)
.post({ data: encodeData })
.json();
if (data.value.code === 200) {
queryId.value = data.value.data.id
queryId.value = data.value.data.id;
if (authorization.value) {
showPayment.value = true
showPayment.value = true;
} else {
showAuthorizationPopup.value = true
showAuthorizationPopup.value = true;
}
}
}
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value)
return
if (isCountingDown.value || !isPhoneNumberValid.value) return;
if (!isPhoneNumberValid.value) {
showToast({ message: "请输入有效的手机号" });
return;
@ -157,30 +391,29 @@ function startCountdown() {
}, 1000);
}
function toUserAgreement() {
uni.navigateTo({
url: '/pages/userAgreement'
})
router.push(`/userAgreement`)
}
function toPrivacyPolicy() {
uni.navigateTo({
url: '/pages/privacyPolicy'
})
router.push(`/privacyPolicy`)
}
function toAuthorization() {
router.push(`/authorization`)
}
//
const agreed = () => {
showAuthorizationPopup.value = false
authorization.value = true
showPayment.value = true
showAuthorizationPopup.value = false;
authorization.value = true;
showPayment.value = true;
};
//
const cancel = () => {
showAuthorizationPopup.value = false
showAuthorizationPopup.value = false;
};
const toExample = () => {
uni.navigateTo({
url: '/pages/example'
})
router.push(`/example?feature=${feature.value}`)
};
onUnmounted(() => {
if (timer) {
@ -194,7 +427,6 @@ onUnmounted(() => {
<div class="mb-6 text-center text-3xl font-bold text-blue-700">
{{ featureData.product_name }}
</div>
<div class="card">
<div class="mb-4 text-lg font-semibold text-gray-800">基本信息</div>
<div class="mb-4 flex items-center" v-if="isHasInput('name')">
@ -205,6 +437,25 @@ onUnmounted(() => {
<label for="idCard" class="form-label">身份证号</label>
<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" />
</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" />
</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" />
</div>
<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" />
</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" />
@ -213,10 +464,38 @@ onUnmounted(() => {
<label for="entCode" class="form-label">统一社会信用代码</label>
<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-popup>
</div>
<div class="mb-4 flex items-center" v-if="isHasInput('carLicense')">
<!-- <label for="entCode" class="form-label">车牌号</label> -->
<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" />
</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" />
</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-popup>
</div>
<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">
@ -224,7 +503,11 @@ onUnmounted(() => {
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重新获取` : '获取验证码' }}
{{
isCountingDown
? `${countdown}s重新获取`
: "获取验证码"
}}
</button>
</div>
</div>
@ -233,8 +516,11 @@ onUnmounted(() => {
<input type="checkbox" v-model="agreeToTerms" />
<span class="ml-2 text-xs text-gray-400">
我已阅读并同意
<span @click="toUserAgreement" class="text-blue-500 underline">用户协议</span>
<span @click="toPrivacyPolicy" class="text-blue-500 underline">隐私政策</span>
<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>
</template>
</span>
</div>
<div class="flex items-center">
@ -247,25 +533,32 @@ onUnmounted(() => {
</div>
</div>
<div class="card mt-4">
<div class="mb-6 text-xl text-gray-800 font-bold">
<div class="mb-4 text-xl text-gray-800 font-bold">
{{ featureData.product_name }}
</div>
<div class="mb-4 text-gray-600 leading-relaxed">
{{ featureData.description }}
<div class="mb-4 flex items-start justify-between">
<div class="text-lg text-gray-500">价格</div>
<div>
<div v-if="discountPrice" class="line-through text-gray-500" :class="{ 'text-lg': discountPrice }">
¥ {{ featureData.sell_price }}
</div>
<div class="text-2xl text-red-600 font-semibold">
¥{{ discountPrice ? (featureData.sell_price * 0.2).toFixed(2) : featureData.sell_price }}
</div>
<div class="mb-6 flex items-center justify-between">
<div class="text-lg text-gray-500">
价格
</div>
<div class="text-lg text-blue-600 font-semibold">
¥{{ featureData.sell_price }}
</div>
<div v-if="discountPrice" class="text-sm text-right text-red-500 mb-4">
限时活动价2折优惠
</div>
<div class="mb-4 text-gray-600 leading-relaxed" v-html="featureData.description">
</div>
<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"
@ -277,6 +570,7 @@ onUnmounted(() => {
{{ feature.name }}
</div>
</div>
</template>
</div>
</div>
<!-- 底部弹出 -->
@ -292,12 +586,16 @@ onUnmounted(() => {
@apply w-20 text-sm font-medium text-gray-700 flex-shrink-0;
}
.form-input::placeholder {
color: var(--van-text-color-3);
}
.form-input {
@apply w-full border-b border-gray-200 px-2 py-2 focus:outline-none;
}
.inquire-bg {
background: url("@/assets/images/inquire_banner_2.png") no-repeat;
background: url("@/assets/images/bg_2.png") no-repeat;
background-position: center;
background-size: cover;
}

209
src/views/Login.vue Normal file
View File

@ -0,0 +1,209 @@
<script setup>
import { ref, computed, onUnmounted } from 'vue'
const router = useRouter()
const phoneNumber = ref('')
const verificationCode = ref('')
const password = ref('')
const isPasswordLogin = ref(false)
const isAgreed = ref(false)
const isCountingDown = ref(false)
const countdown = ref(60)
let timer = null
//
const phoneFocused = ref(false)
const codeFocused = ref(false)
const passwordFocused = ref(false)
const isPhoneNumberValid = computed(() => {
return /^1[3-9]\d{9}$/.test(phoneNumber.value)
})
const canLogin = computed(() => {
if (!isPhoneNumberValid.value) return false
if (isPasswordLogin.value) {
return password.value.length >= 6
} else {
return verificationCode.value.length === 6
}
})
async function sendVerificationCode() {
if (isCountingDown.value || !isPhoneNumberValid.value) return
if (!isPhoneNumberValid.value) {
showToast({ message: "请输入有效的手机号" });
return
}
const { data, error } = await useApiFetch('auth/sendSms')
.post({ mobile: phoneNumber.value, actionType: 'login' })
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
showToast({ message: "获取成功" });
startCountdown()
} else {
showToast(data.value.msg)
}
}
}
function startCountdown() {
isCountingDown.value = true
countdown.value = 60
timer = setInterval(() => {
if (countdown.value > 0) {
countdown.value--
} else {
clearInterval(timer)
isCountingDown.value = false
}
}, 1000)
}
async function handleLogin() {
if (!isPhoneNumberValid.value) {
showToast({ message: "请输入有效的手机号" });
return
}
if (isPasswordLogin.value) {
if (password.value.length < 6) {
showToast({ message: "密码长度不能小于6位" });
return
}
} else {
if (verificationCode.value.length !== 6) {
showToast({ message: "请输入有效的验证码" });
}
}
if (!isAgreed.value) {
showToast({ message: "请先同意用户协议" });
return
}
const { data, error } = await useApiFetch('/user/mobileCodeLogin')
.post({ mobile: phoneNumber.value, code: verificationCode.value })
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
localStorage.setItem('token', data.value.data.accessToken)
localStorage.setItem('refreshAfter', data.value.data.refreshAfter)
// if (phoneNumber.value === "18276151590") {
// localStorage.setItem('m', "shifenliangzai")
// }
window.location.href = '/'
}
}
}
function toUserAgreement() {
router.push(`/userAgreement`)
}
function toPrivacyPolicy() {
router.push(`/privacyPolicy`)
}
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
})
const onClickLeft = () => {
router.replace('/')
}
</script>
<template>
<div class="login-layout ">
<van-nav-bar fixed placeholder title="用户登录" left-text="" left-arrow @click-left="onClickLeft" />
<div class="login px-8">
<div class="mb-8 pt-8 text-left">
<div class="flex flex-col items-center">
<img class="h-16 w-16 rounded-full shadow" src="@/assets/images/logo.png" alt="Logo" />
<img class="mt-4 h-10" src="@/assets/images/logo_title.png" alt="Title" />
</div>
</div>
<div class="space-y-5">
<!-- 手机号输入 -->
<div :class="['input-container bg-blue-300/20', phoneFocused ? 'focused' : '']">
<input v-model="phoneNumber" class="input-field" type="tel" placeholder="请输入手机号" maxlength="11"
@focus="phoneFocused = true" @blur="phoneFocused = false" />
</div>
<!-- 验证码输入 -->
<div v-if="!isPasswordLogin">
<div class="flex items-center justify-between">
<div :class="['input-container bg-blue-300/20', codeFocused ? 'focused' : '']">
<input v-model="verificationCode" class="input-field" placeholder="请输入验证码" maxlength="6"
@focus="codeFocused = true" @blur="codeFocused = false" />
</div>
<button
class="ml-2 px-4 py-2 text-sm font-bold flex-shrink-0 rounded-lg transition duration-300"
:class="isCountingDown || !isPhoneNumberValid ? 'cursor-not-allowed bg-gray-300 text-gray-500' : 'bg-blue-500 text-white hover:bg-blue-600'"
@click="sendVerificationCode">
{{ isCountingDown ? `${countdown}s重新获取` : '获取验证码' }}
</button>
</div>
</div>
<!-- 密码输入 -->
<div v-if="isPasswordLogin" :class="['input-container', passwordFocused ? 'focused' : '']">
<input v-model="password" class="input-field" type="password" placeholder="请输入密码"
@focus="passwordFocused = true" @blur="passwordFocused = false" />
</div>
<!-- 协议同意框 -->
<div class="flex items-start space-x-2">
<input type="checkbox" v-model="isAgreed" class="mt-1" />
<span class="text-xs text-gray-400 leading-tight">
未注册手机号登录后将自动生成账号并且代表您已阅读并同意
<a class="cursor-pointer text-blue-400" @click="toUserAgreement">
用户协议
</a>
<a class="cursor-pointer text-blue-400" @click="toPrivacyPolicy">
隐私政策
</a>
</span>
</div>
</div>
<button
class="mt-20 w-full py-3 text-lg font-bold text-white bg-blue-500 rounded-full transition duration-300"
@click="handleLogin">
登录
</button>
</div>
</div>
</template>
<style scoped>
.login-layout {
background: url("@/assets/images/login_bg.png") no-repeat;
background-position: center;
background-size: cover;
height: 100vh;
}
.input-container {
border: 2px solid rgba(125, 211, 252, 0.0);
border-radius: 1rem;
transition: duration-200;
}
.input-container.focused {
border: 2px solid #3b82f6;
}
.input-field {
width: 100%;
padding: 1rem;
background: transparent;
border: none;
outline: none;
transition: border-color 0.3s ease;
}
</style>

129
src/views/Me.vue Normal file
View File

@ -0,0 +1,129 @@
<template>
<div class="box-border min-h-screen from-blue-100 to-white bg-gradient-to-b">
<div class="flex flex-col p-4">
<!-- Profile Section -->
<div class="profile-section mb-4 flex items-center gap-4 rounded-md bg-white p-4 shadow-md"
@click="!isLoggedIn ? redirectToLogin() : null">
<img :src="userAvatar" alt="User Avatar" class="rounded-full" width="100" height="100" />
<div>
<h2 class="text-lg font-bold">
{{ isLoggedIn ? maskName(userName) : '点击登录' }}
</h2>
</div>
</div>
<!-- Divider (Optional) -->
<hr />
<!-- Features Section -->
<div class="features-section flex flex-col gap-2">
<div v-for="(feature, index) in features" :key="index"
class="feature-item rounded-md bg-white p-3 shadow-sm cursor-pointer" @click="feature.action">
<div class="flex items-center gap-2">
<!-- <van-icon :name="feature.icon" /> -->
<span>{{ feature.title }}</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const router = useRouter()
const userName = ref('点击登录')
const userAvatar = ref('https://img0.baidu.com/it/u=1240274933,2284862568&fm=253&fmt=auto&app=138&f=PNG?w=180&h=180')
const isLoggedIn = ref(false)
const features = ref([
{ title: '我的报告', icon: 'list', action: () => toHistory() },
{ title: '联系客服', icon: 'service-o', action: () => toService() },
{ title: '用户协议', icon: 'file', action: () => toUserAgreement() },
{ title: '退出登录', icon: 'logout', action: () => handleLogout() },
])
const maskName = computed(() => {
return (name) => {
return name.substring(0, 3) + "****" + name.substring(7);
}
})
function toHistory() {
router.push(`/historyQuery`)
}
function toUserAgreement() {
router.push(`/userAgreement`)
}
function redirectToLogin() {
router.push(`/login`)
}
function handleLogout() {
localStorage.removeItem('token') // 使 localStorage token
isLoggedIn.value = false
userName.value = '点击登录'
userAvatar.value = 'https://img0.baidu.com/it/u=1240274933,2284862568&fm=253&fmt=auto&app=138&f=PNG?w=180&h=180'
}
function toService() {
window.location.href = '/service' //
}
async function fetchUserInfo() {
const { data, error } = await useApiFetch('/user/detail')
.get()
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
const userinfo = data.value.data.userInfo
userName.value = userinfo.nickName || ''
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
}
}
}
const safeAreaTop = ref(0)
onMounted(() => {
const token = localStorage.getItem('token') // 使 localStorage token
if (token) {
isLoggedIn.value = true
fetchUserInfo()
} else {
isLoggedIn.value = false
}
})
</script>
<style scoped>
.profile-section {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background-color: white;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.features-section {
display: flex;
flex-direction: column;
gap: 1rem;
}
.feature-item {
background-color: white;
padding: 1rem;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.feature-item:hover {
background-color: #f0f0f0;
}
</style>

View File

@ -1,6 +1,5 @@
<script setup>
import { useWebView } from "@/composables/useWebView";
useWebView()
</script>
<template>
@ -95,30 +94,7 @@ useWebView()
当您在使用全能查服务过程中为向您提供您需求的全能查软件服务交互展示搜索结果识别账号异常状态维护全能查服务的正常运行改进及优化您对全能查服务的体验并保障您的账号安全包括您使用全能查服务以及使用方式的信息并将这些信息进行关联
</div>
<div class="leading-relaxed">
<div>1使用信息</div>
<div>
在提供服务过程中为了保证您能正常使用产品功能我们可能会向系统申请您的设备权限可能包括但不限于IMEIIMSI设备MAC地址软件列表设备序列号android
ID等申请前我们会通过弹窗或页面说明的方式征询您的同意你可以选择允许禁止权限申请因少量设备厂商在应用安装时就默认您给予了部分基础权限不支持弹窗确认功能具体我们建议您关注您的设备功能说明及权限列表如您不同意给我们这些权限的您可以及时通过设备中设置的路径取消对我们的授权支持弹窗功能的设备权限申请成功后您可以随时进入设备设置-权限管理中关闭相应权限权限关闭后相关产品功能可能无法正常使用在不同设备中权限显示方式及关闭方式可能有所不同具体请您参考设备及系统开发方说明或指引
</div>
<div>
提供服务过程中主要涉及的访问权限如下
基于设备相机及相册权限的附加功能出于您的使用需求当您需要在使用在线客服的相机相册时我们需要取得您的设备相机或相册权限并收集您提供的图文和/或视频信息如您拒绝提供仅会使您无法在相关功能中添加图片和/或视频但并不影响您正常使用其他功能
</div>
<div>
基于设备麦克风权限的附加功能若您需要在使用客服电话服务时我们需要取得您的设备麦克风权限并收集您的语音信息如您拒绝提供仅会使您无法使用相关语音功能但并不影响您正常使用其他功能
</div>
<div>
您理解并同意前述涉及设备权限相机摄像头相册麦克风通讯录的授权需要您在您的设备中打开相关访问权限以实现这些权限所涉及信息的收集与使用您亦可以在您的设备中查看或调整前述权限状态您了解并知悉一旦您开启权限则表示您授权我们可以收集和使用相关个人信息来为您提供相关服务一旦您关闭该权限则表示您取消了授权我们将不再基于该权限继续收集和使用相关个人信息也无法为您提供对应的服务与功能但是您的关闭行为并不会影响此前基于您的授权行为所进行的信息收集与使用
</div>
</div>
<div class="leading-relaxed">
<div>2设备信息</div>
<div>
我们会根据您在软件安装及使用中授予的具体权限接收并记录您所使用的设备相关信息例如设备型号操作系统版本设备设置唯一设备标识符等软硬件特征信息设备所在位置相关信息例如IP地址GPS/北斗位置信息以及能够提供相关信息的Wi-Fi接入点蓝牙和基站等传感器信息
</div>
</div>
<div class="leading-relaxed">
<div>3日志信息</div>
<div>1日志信息</div>
<div>
当您使用我们的网站或客户端提供的产品或服务时我们会自动收集您对我们服务的详细使用情况作为有关网络日志保存例如您的搜索查询内容IP地址使用的语言访问日期和时间您访问的网页记录日志信息
</div>
@ -127,180 +103,14 @@ useWebView()
</div>
</div>
<div class="leading-relaxed">
<div>4您向我们提供的信息</div>
<div>2您向我们提供的信息</div>
<div>
在服务使用过程中您可以对全能查产品及服务的体验问题反馈帮助我们更好地了解您使用我们产品或服务的体验和需求改善我们产品或服务,为此我们会记录您的联系信息反馈的问题或建议以便我们进一步联系您反馈您我们的处理意见
为向您提供更好的服务例如在不同的服务端或设备上提供体验一致的服务和您需求的客服接待了解产品适配性识别账号异常状态
</div>
</div>
<div class="leading-relaxed">
<div>5第三方软件开发包SDK</div>
<div>
我们产品中可能会包含第三方SDK或其他类似的应用程序如您在我们平台上使用这类由第三方提供的服务时您同意将由其直接收集和处理您的信息如以嵌入代码插件等形式例如当您使用支付宝账号授权我们的产品时支付宝SDK需要收集您的账号信息前述服务商收集和处理信息等行为遵守其自身的隐私条款而不适用于本政策但我们也会努力审查该第三方的业务准入资质并努力要求该服务商的合法合规性与安全性为了最大程度保障您的信息安全我们强烈建议您在使用任何第三方SDK类服务前先行查看其隐私条款为保障您的合法权益如您发现这等SDK或其他类似的应用程序存在风险时建议您立即终止相关操作并及时与我们取得联系
</div>
<div>以下是我们目前接入的第三方SDK类服务商的信息</div>
<div class="mb-4">
<div class="mb-2 text-center text-xl font-bold leading-relaxed">
权限清单
</div>
<div class="w-full table border-collapse border border-gray-300">
<div class="table-row bg-gray-100">
<div class="table-cell border border-gray-300 p-2 font-bold">
权限名称
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
权限功能说明
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
使用场景或目的
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
使用平台
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
读取/写入外置存储
</div>
<div class="table-cell border border-gray-300 p-2">
读取和写入设备储存空间内的数据
</div>
<div class="table-cell border border-gray-300 p-2">
用于保障全能查的稳定运行以便用户在使用功能时可读取写入/下载/保存/修改图片文件崩溃日志等信息
</div>
<div class="table-cell border border-gray-300 p-2">
AndroidiOS
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
位置
</div>
<div class="table-cell border border-gray-300 p-2">
通过GPS或网络位置信息获取设备所在的地理位置信息
</div>
<div class="table-cell border border-gray-300 p-2">
用于安全保障提供精准服务和内容
</div>
<div class="table-cell border border-gray-300 p-2">
AndroidiOS
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
摄像头
</div>
<div class="table-cell border border-gray-300 p-2">
使用摄像头拍摄图片
</div>
<div class="table-cell border border-gray-300 p-2">
用于完成照片的拍摄和发布
</div>
<div class="table-cell border border-gray-300 p-2">
AndroidiOS
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
读取/写入相册
</div>
<div class="table-cell border border-gray-300 p-2">
读取和写入设备相册中的内容
</div>
<div class="table-cell border border-gray-300 p-2">
用于设置头像图片以及完成照片的发布
</div>
<div class="table-cell border border-gray-300 p-2">
AndroidiOS
</div>
</div>
</div>
</div>
<div class="mb-4">
<div class="mb-2 text-center text-xl font-bold leading-relaxed">
Android操作系统第三方SDK列表
</div>
<div class="w-full table border-collapse border border-gray-300">
<div class="table-row bg-gray-100">
<div class="table-cell border border-gray-300 p-2 font-bold">
第三方SDK名称
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
应用场景
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
收集个人信息的类型
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
第三方SDK提供方
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
隐私政策链接
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
支付宝SDK
</div>
<div class="table-cell border border-gray-300 p-2">
提供支付宝支付提现能力
</div>
<div class="table-cell border border-gray-300 p-2">
网络状态信息设备信息本机号码
</div>
<div class="table-cell border border-gray-300 p-2">
支付宝
</div>
<div class="table-cell border border-gray-300 p-2 text-blue-500">
<a href="https://render.alipay.com/p/c/k2cx0tg8" target="_blank">查看隐私政策</a>
</div>
</div>
</div>
</div>
<div class="mb-4">
<div class="mb-2 text-center text-xl font-bold leading-relaxed">
IOS操作系统第三方SDK列表
</div>
<div class="w-full table border-collapse border border-gray-300">
<div class="table-row bg-gray-100">
<div class="table-cell border border-gray-300 p-2 font-bold">
第三方SDK名称
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
应用场景
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
收集个人信息的类型
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
第三方SDK提供方
</div>
<div class="table-cell border border-gray-300 p-2 font-bold">
隐私政策链接
</div>
</div>
<div class="table-row">
<div class="table-cell border border-gray-300 p-2">
支付宝SDK
</div>
<div class="table-cell border border-gray-300 p-2">
提供支付宝支付提现能力
</div>
<div class="table-cell border border-gray-300 p-2">
网络状态信息设备信息本机号码
</div>
<div class="table-cell border border-gray-300 p-2">
支付宝
</div>
<div class="table-cell border border-gray-300 p-2 text-blue-500">
<a href="https://render.alipay.com/p/c/k2cx0tg8" target="_blank">查看隐私政策</a>
</div>
</div>
</div>
</div>
</div>
<div class="leading-relaxed">
<div>6为您提供安全保障收集信息</div>
<div>3为您提供安全保障收集信息</div>
<div>
为预防发现调查欺诈侵权危害安全非法或违反与我们或与我们关联公司的协议政策或规则的行为我们可能收集或整合您的用户个人信息服务使用信息设备信息日志信息以及我们关联公司合作伙伴取得您授权或依据法律共享的信息
您理解并同意我们向您提供的功能和服务场景是不断迭代升级的如我们未在上述场景中明示您需要收集的个人信息我们将会通过页面提示交互设计等方式另行向您明示信息收集的内容范围和目的并征得您同意
@ -309,6 +119,18 @@ useWebView()
如我们停止运营全能查产品或服务我们将及时停止继续收集您个人信息的活动将停止运营的通知以公告或短信的形式通知您并依照所适用的法律对所持有的您的个人信息进行删除或匿名化处理
</div>
</div>
<div class="leading-relaxed">
<div>4手机号码收集及其用途</div>
<div>
在您使用全能查服务的过程中我们可能会要求您提供手机号码我们收集您的手机号码主要是为了向您发送重要的通知服务更新账户安全信息促销活动服务相关的短信等为了确保您能及时获得关于您账号安全产品更新和优化系统维护等信息我们可能会向您发送有关服务变更功能更新版本升级等通知确保您能够持续享受我们的产品和服务
</div>
<div>
此外您的手机号码还可能用于为您提供个性化的短信推广内容帮助您了解我们新推出的服务产品或活动优惠我们承诺不会在未经您明确同意的情况下将您的手机号码用于任何与服务相关以外的用途且不会将您的信息出售或租赁给第三方为了保障您的权益您可以随时通过设置页面或联系客户服务停止接收短信通知或推广信息如果您选择取消订阅短信通知或推广您仍将继续收到与账户安全系统通知等相关的重要信息
</div>
<div>
我们会采取严格的措施保护您的手机号码不被滥用包括采用加密存储定期审查访问权限等技术和管理手段以确保您的个人信息安全同时我们也会根据适用的法律法规在您停止使用我们的服务或终止您的账户时删除或匿名化处理您的手机号码及其他相关信息
</div>
</div>
</div>
<div class="mb-4">
<!-- 第二部分 -->

View File

@ -2,7 +2,7 @@
const featureMap = {
G09SC02: {
name: '单人婚姻',
component: defineAsyncComponent(() => import('@/ui/CBad.vue')),
component: defineAsyncComponent(() => import('@/ui/CMarriage.vue')),
},
G27BJ05: {
name: '借贷意向',
@ -32,34 +32,83 @@ const featureMap = {
},
G05HZ01: {
name: '股东人企关系',
component: defineAsyncComponent(() =>
import('@/ui/CRelatedEnterprises.vue')
),
component: defineAsyncComponent(() => import('@/ui/CRelatedEnterprises.vue')),
},
Q23SC01: {
name: '企业涉诉',
component: defineAsyncComponent(() => import('@/ui/CLawsuit.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')),
}
};
import LEmpty from "@/components/LEmpty.vue";
import { useWebView } from "@/composables/useWebView";
useWebView()
import LPendding from "@/components/LPendding.vue";
import LTitle from "@/components/LTitle.vue";
const route = useRoute();
const productId = ref(null);
const isDone = ref(true);
const reportData = ref([])
const reportParams = ref({})
const reportName = ref("")
const reportDateTime = ref(null)
const orderId = ref(null);
const orderNo = ref("")
const isEmpty = ref(false)
const isPending = ref(false)
onMounted(() => {
const query = new URLSearchParams(window.location.search);
orderNo.value = query.get("out_trade_no");
orderId.value = query.get("order_id");
orderNo.value = query.get("order_no");
if (!orderNo.value && !orderId.value) {
orderId.value = route.query.orderId;
orderNo.value = route.query.orderNo;
}
if (!orderId.value && !orderNo.value) return;
getReport()
@ -80,48 +129,176 @@ const getReport = async () => {
if (data.value && !error.value) {
if (data.value.code === 200) {
productId.value = data.value.data.product_id;
if (data.value.data.product_name === '婚姻评估') {
reportData.value = data.value.data.query_data.reverse()
} else {
reportData.value = data.value.data.query_data
}
productId.value = data.value.data.product_id;
reportParams.value = data.value.data.query_params
reportName.value = data.value.data.product_name
reportDateTime.value = data.value.data.create_time
} else if (data.value.code === 200003) {
isEmpty.value = true
} else if (data.value.code === 200002) {
isPending.value = true
}
}
}
//
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") {
// 64
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") {
// 64
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "ent_name") {
// 33 "*"
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); // 633
}
} else if (type === "ent_code") {
// 44 "*"
if (value.length <= 8) {
return value.slice(0, 4) + "*".repeat(value.length - 4); // 84 *
} else {
return value.slice(0, 4) + "*".repeat(value.length - 8) + value.slice(-4); // 844
}
} else if (type === "car_license") {
// 22 "*"
if (value.length <= 4) {
return value[0] + "*".repeat(value.length - 1); // 4
} else {
// 22 "*"
return value.slice(0, 2) + "*".repeat(value.length - 4) + value.slice(-2);
}
}
return value;
}
});
</script>
<template>
<div class="min-h-full from-blue-100 to-white bg-gradient-to-b">
<van-notice-bar color="#e03131" background="#ecf9ff" left-icon="info-o" text="由于全能查APP暂未上线建议将此网站加入收藏夹或书签" />
<!-- <CTabs :tabs="sortedTabs" type="blue-green" /> -->
<template v-if="isDone">
<div class="flex flex-col gap-y-4 p-4 pt-12">
<div class="flex flex-col gap-y-4 p-4">
<template v-if="!isEmpty && !isPending">
<div id="overdiv" class="title">报告概述</div>
<div class="card">
<div class="flex flex-col gap-y-2">
<div class="flex justify-between">
<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">2024年11月18日 23:11:23</span>
<span class="text-gray-600">{{ reportDateTime }}</span>
</div>
<div class="flex justify-between">
<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>
<div class="mt-2" v-if="reportData.length > 1">
<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>
<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>
</template>
</div>
</div>
</template>
<LEmpty v-if="isEmpty" />
<LPendding v-if="isPending" />
<template v-for="(item, index) in reportData" :key="index">
<div id="lawsuit" class="title">{{ featureMap[item.apiID].name }}</div>
<component :is="featureMap[item.apiID].component" :data="item.data"></component>
<component :is="featureMap[item.apiID].component" :data="item.data" :params="reportParams">
</component>
</template>
<div class="card">
<div>
@ -145,10 +322,41 @@ const getReport = async () => {
</div>
</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;
}
</style>

View File

@ -7,6 +7,7 @@
</template>
<script setup>
onMounted(() => {
//
(function (d, t) {
@ -47,9 +48,11 @@ onMounted(() => {
font-size: 16px;
color: #333;
}
iframe {
background: #000000;
}
iframe::content .file-uploads.file-uploads-html5.text-black-900 {
display: none !important;
}

View File

@ -1,6 +1,5 @@
<script setup>
import { useWebView } from "@/composables/useWebView";
useWebView()
</script>
<template>
@ -241,7 +240,7 @@ useWebView()
<div class="leading-relaxed">
如您对本协议及本服务有任何问题请通过邮箱
<text class="text-blue-500"> admin@iieeii.com </text>
APP联系客服联系海南省学宇思网络科技有限公司进行咨询
通过联系客服联系海南省学宇思网络科技有限公司进行咨询
海南省学宇思网络科技有限公司会尽最大努力解决您的问题
</div>
</div>

118
src/views/index.vue Normal file
View File

@ -0,0 +1,118 @@
<script setup>
const router = useRouter();
function toInquire(name) {
if (name === 'Marriage') {
router.push(`/inquire/toc_Marriage`);
}
else {
router.push(`/list${name}`,);
}
}
// 30
onMounted(() => {
})
function toHistory() {
router.push(`/historyQuery`);
}
</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('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('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>
<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">
查询记录有效期为30天
</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-right {
clip-path: polygon(0 0, 0 0, 90% 100%, 0 0);
}
</style>

View File

@ -22,20 +22,24 @@
本平台提供全方位司法涉诉查询服务助您全面防范功能包括
</p>
<ul class="list-disc list-inside mt-4 space-y-2 text-gray-600">
<li>个人涉诉查看XXXXXXXXXXXXXXXXXXX</li>
<li>企业涉诉查看XXXXXXXXXXXXXXXXXXX</li>
<li>
<strong>个人涉诉</strong> 查看个人是否涉及诉讼案件了解其法律纠纷情况帮助您判断该个人的法律风险
</li>
<li>
<strong>企业涉诉</strong> 查看企业是否涉及诉讼案件了解企业的法律风险及潜在的经营问题帮助您评估与该企业合作的安全性
</li>
</ul>
</div>
</div>
</div>
</template>
<script setup>
import { useWebView } from "@/composables/useWebView";
useWebView()
import grssIcon from '@/assets/images/grss_icon.svg';
import qyssIcon from '@/assets/images/qyss_icon.svg';
const router = useRouter()
const menuItems = [
{ title: "个人涉诉", icon: grssIcon, product: "toc_PersonalLawsuit" },
{ title: "企业涉诉", icon: qyssIcon, product: "toc_EnterpriseLawsuit" },
@ -43,7 +47,7 @@ const menuItems = [
const toInquire = (product) => {
if (!product) return
uni.navigateTo({ url: '/pages/inquire?p=' + product })
router.push('/inquire/' + product)
}
</script>

View File

@ -43,7 +43,6 @@ const menuItems = [
{ title: "出险信息", icon: "/static/icon-accident.png", textColor: "text-red-500" },
{ title: "维修记录", icon: "/static/icon-maintain.png", textColor: "text-blue-500" },
];
// console.log("uni", uni.navigateTo({ url: "/pages/index" }))
</script>
<style scoped>

View File

@ -24,8 +24,12 @@
本平台提供全方位风险评估查询服务助您全面防范功能包括
</p>
<ul class="list-disc list-inside mt-4 space-y-2 text-gray-600">
<li>个人不良查看XXXXXXXXXXXXXXXXXXX</li>
<li>股东人企关系查看XXXXXXXXXXXXXXXXXXX</li>
<li>
<strong>个人不良记录查询</strong> 通过查询个人的不良记录评估其风险等级高风险帮助您识别潜在的信用和法律风险确保合作方的合法合规
</li>
<li>
<strong>股东人企关系查询</strong> 通过个人关联的企业了解其可能涉及的风险帮助您判断该个人的风险水平特别是与高风险企业有联系的个人
</li>
</ul>
</div>
</div>
@ -33,18 +37,16 @@
</template>
<script setup>
import { useWebView } from "@/composables/useWebView";
useWebView()
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 toInquire = (product) => {
uni.navigateTo({ url: '/pages/inquire?p=' + product })
router.push('/inquire/' + product)
}
</script>

View File

@ -8,7 +8,8 @@
<!-- 功能菜单 -->
<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">
<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">
</div>
@ -23,9 +24,22 @@
本平台提供全方位核验工具服务助您全面防范功能包括
</p>
<ul class="list-disc list-inside mt-4 space-y-2 text-gray-600">
<li>手机三要素核验查看XXXXXXXXXXXXXXXXXXX</li>
<li>银行卡黑名单查看XXXXXXXXXXXXXXXXXXX</li>
<li><strong>手机三要素</strong> 查询个人与手机号码是否匹配确保手机号码的真实性</li>
<li><strong>银行卡黑名单</strong> 查询银行卡是否被列入黑名单帮助您识别潜在的金融风险</li>
<li><strong>身份证二要素</strong> 验证姓名与身份证号码的匹配情况确保身份真实性</li>
<li><strong>手机二要素</strong> 查询手机号码和身份证的匹配情况用于验证身份的真实性</li>
<li><strong>在网时长</strong> 查询手机号码的在网时长帮助评估号码的稳定性与历史</li>
<li><strong>手机二次卡</strong> 检查手机是否有二次卡防止诈骗等风险</li>
<li><strong>手机号码风险</strong> 评估手机号码的潜在风险包括是否与违法行为关联</li>
<li><strong>银行卡四要素</strong> 通过四要素核验银行卡与个人是否匹配进一步验证银行账户的真实性</li>
<li><strong>人车核验</strong> 查询个人与车辆的关联情况帮助识别车辆的所有权和风险</li>
<li><strong>名下车辆</strong> 查询个人名下的所有车辆信息了解其车辆资产</li>
<li><strong>双人婚姻</strong> 查询双人婚姻状况帮助评估婚姻的法律状态和风险</li>
<!-- <li><strong>自然人生存状态</strong> 查询个人的生存状态帮助验证其是否真实存在</li> -->
<!-- <li><strong>银行卡三要素</strong> 查询银行卡的三要素验证账户信息的安全性</li> -->
<!-- <li><strong>学历核验</strong> 验证学历信息的真实性确保所提供的学历符合实际</li> -->
</ul>
</div>
</div>
</div>
@ -46,43 +60,28 @@ import xlhyIcon from '@/assets/images/xlhy_icon.svg'; // 学历核验
import rchyIcon from '@/assets/images/rchy_icon.svg'; //
import mxclIcon from '@/assets/images/mxcl_icon.svg'; //
import srhyIcon from '@/assets/images/srhy_icon.svg'; //
const router = useRouter()
const menuItems = [
{ title: "手机三要素", icon: sjsysIcon },
{ title: "银行卡黑名单", icon: yhkhmdIcon },
{ title: "身份证二要素", icon: sfzeysIcon },
{ title: "手机二要素", icon: sjeysIcon },
{ title: "在网时长", icon: sjzwscIcon },
{ title: "手机二次卡", icon: sjeckIcon },
{ title: "手机号码风险", icon: sjhmfxIcon },
{ title: "银行卡四要素", icon: yhk4ysIcon },
{ title: "银行卡三要素", icon: yhksysIcon },
{ title: "自然人生存状态", icon: zrrscztIcon },
{ title: "学历核验", icon: xlhyIcon },
{ title: "人车核验", icon: rchyIcon },
{ title: "名下车辆", icon: mxclIcon },
{ title: "双人婚姻", icon: srhyIcon },
{ title: "手机三要素", icon: sjsysIcon, product: "toc_PhoneThreeElements" },
{ title: "银行卡黑名单", icon: yhkhmdIcon, product: "toc_BankCardBlacklist" },
{ title: "身份证二要素", icon: sfzeysIcon, product: "toc_IDCardTwoElements" },
{ title: "手机二要素", icon: sjeysIcon, product: "toc_PhoneTwoElements" },
{ title: "在网时长", icon: sjzwscIcon, product: "toc_NetworkDuration" },
{ title: "手机二次卡", icon: sjeckIcon, product: "toc_PhoneSecondaryCard" },
{ title: "手机号码风险", icon: sjhmfxIcon, product: "toc_PhoneNumberRisk" },
{ title: "银行卡四要素", icon: yhk4ysIcon, product: "toc_BankCardFourElements" },
// { title: "", icon: zrrscztIcon, product: "toc_NaturalLifeStatus" },
// { title: "", icon: yhksysIcon, product: "toc_BankCardThreeElements" },
// { title: "", icon: xlhyIcon, product: "toc_EducationVerification" },
{ title: "人车核验", icon: rchyIcon, product: "toc_PersonVehicleVerification" },
{ title: "名下车辆", icon: mxclIcon, product: "toc_VehiclesUnderName" },
{ title: "双人婚姻", icon: srhyIcon, product: "toc_DualMarriage" },
];
onMounted(() => {
uni.getEnv((platform) => {
console.log("当前运行环境", platform)
handleBridgeReady(platform)
});
})
const handleBridgeReady = (platform) => {
if (platform.h5) {
window.parent.postMessage({ loaded: true }, '*');
}
// else {
// uni.postMessage({
// data: {
// loaded: true,
// },
// });
// }
};
// console.log("uni", uni.navigateTo({ url: "/pages/index" }))
const toInquire = (product) => {
router.push('/inquire/' + product)
}
</script>
<style scoped></style>

View File

@ -14,14 +14,20 @@ export default defineConfig({
port: 5678, // 自定义端口号,可选
strictPort: true, // 如果端口被占用则抛出错误而不是使用下一个可用端口
proxy: {
// '/api/v1': {
// target: 'https://app.quannengcha.com', // 本地接口地址
// changeOrigin: true,
// },
'/api/v1': {
target: 'http://127.0.0.1:8888', // 本地接口地址
target: 'https://www.quannengcha.com', // 本地接口地址
changeOrigin: true,
},
'/api/v1/chat': {
target: 'https://www.quannengcha.com', // 本地接口地址
changeOrigin: true,
// rewrite: (path) => path.replace(/^\/api\/v1\/chat/, '/chat')
},
// '/api/v1': {
// target: 'https://6m4685017o.goho.co', // 本地接口地址
// changeOrigin: true,
// },
},
},
plugins: [