提交 d164fb2a authored 作者: huhuiqing's avatar huhuiqing

Merge branch 'master' of http://8.140.26.4:10003/caijian/risk-monitor into dev_hhq

File added
......@@ -322,9 +322,10 @@ export function getCompareCountSan(startTime) {
return request200(
request({
method: "GET",
url: "/api/entitiesDataCount/compareCountSan",
// url: "/api/entitiesDataCount/compareCountSan",
url: "/api/entitiesDataInfo/getSanCountInfo",
params: {
startTime
sanctionDate: startTime || "2025-11-11"
}
})
);
......@@ -385,6 +386,30 @@ export function getEntitiesUpdateCount() {
);
}
/**
* 制裁领域分析
*/
export function getSanDomainCount() {
return request200(
request({
method: "GET",
url: "/api/entitiesDataCount/getSanDomainCount"
})
);
}
/**
* 上市企业制裁强度
*/
export function getSanStrength() {
return request200(
request({
method: "GET",
url: "/api/entitiesDataInfo/listedEntity/sanInfo"
})
);
}
/**
* 新增实体领域分布情况
* @param {string} sanTime - 开始时间,格式为 'YYYY-MM-DD'
......@@ -416,7 +441,7 @@ export function getEntitiesDomainCount() {
return request200(
request({
method: "GET",
url: "/api/entitiesDataCount/entitiesDomainCount"
url: "/api/entitiesDataInfo/getPreviousDomian"
})
);
}
......@@ -554,3 +579,31 @@ export function getEntityFinancing() {
})
);
}
/**
* 上市企业市值变化情况
*/
export function getEntityMarketValue() {
return request200(
request({
method: "GET",
url: "/api/entitiesDataInfo/listedEntity/market"
})
);
}
/**
* 重点上市企业列表
*/
export function getKeyListedEntityList(date, keyword = "") {
return request200(
request({
method: "GET",
url: "/api/entitiesDataInfo/listedEntity/keyEntity",
params: {
sanctionDate: date,
searchText: keyword
}
})
);
}
......@@ -21,19 +21,19 @@
<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import * as echarts from "echarts";
import Center from "./assets/埃隆·马斯克.png";
import P1 from "./assets/唐纳德·特朗普.png";
import P2 from "./assets/詹姆斯・默多克.png";
import P3 from "./assets/格温・肖特韦尔.png";
import P4 from "./assets/金博尔・马斯克.png";
import P5 from "./assets/拉里・埃里森.png";
import P6 from "./assets/斯科特·贝森特.png";
import P7 from "./assets/杰弗里·凯斯勒.png";
import P8 from "./assets/马尔科·卢比奥.png";
import P9 from "./assets/道格·伯格姆.png";
import P10 from "./assets/艾拉・埃伦普里斯.png";
import P11 from "./assets/贾斯汀・马斯克.png";
import PS from "./assets/史蒂夫・尤尔韦松.png";
import Center from "./assets/img1.png";
import P1 from "./assets/img2.png";
import P2 from "./assets/img3.png";
import P3 from "./assets/img4.png";
import P4 from "./assets/img5.png";
import P5 from "./assets/img6.png";
import P6 from "./assets/img7.png";
import P7 from "./assets/img8.png";
import P8 from "./assets/img9.png";
import P9 from "./assets/img10.png";
import P10 from "./assets/img11.png";
import P11 from "./assets/img12.png";
import PS from "./assets/img13.png";
const list = ref(["圆形布局", "力导向布局", "树形布局"]);
const activeIndex = ref("圆形布局");
......
......@@ -562,51 +562,124 @@
</div>
</div>
<div class="home-main-footer-main">
<div class="content-header">
<div class="icon">
<img src="./assets/images/box1-header-icon.png" alt="" />
</div>
<div class="title">{{ "政令库" }}</div>
</div>
<div class="content-box">
<div class="main-item" v-for="(decree, index) in curDecreeList" :key="index" @click="handleClickToDetail">
<div class="main-item-left">
<div
class="left-box"
:class="{
type1: decree.status === 1,
type2: decree.status === 2,
type3: decree.status === 3
}"
>
{{ decree.type }}
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "发布时间" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox
v-for="time in pubTime"
:key="time.id"
v-model="activePubTime"
:label="time.id"
class="filter-checkbox"
>
{{ time.name }}
</el-checkbox>
</div>
</div>
<div class="main-item-center">
<div class="main-item-center-box1">{{ decree.title }}</div>
<div class="main-item-center-box2">{{ decree.time }}</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "涉及领域" }}</div>
</div>
<div class="main-item-right">
<div class="main-item-right-text">{{ "查看官网政令原文" }}</div>
<div class="main-item-right-icon">
<img src="./assets/images/open-icon.png" alt="" />
<div class="select-main select-main1">
<div class="checkbox-group">
<el-checkbox
v-for="area in areaList"
:key="area.id"
v-model="activeAreaList"
:label="area.id"
class="filter-checkbox"
>
{{ area.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
<div class="footer-box">
<div class="footer-left">
{{ `共${decreeList.length}项调查` }}
<div class="right">
<div class="content-header">
<div class="icon">
<img src="./assets/images/footer-header-icon.png" alt="" />
</div>
<div class="title">{{ "政令库" }}</div>
</div>
<div class="footer-right">
<el-pagination
@current-change="handleCurrentChange"
:pageSize="10"
:current-page="currentPage"
background
layout="prev, pager, next"
:total="decreeList.length"
/>
<div class="content-box">
<div
class="main-item"
v-for="(item, index) in curDecreeList"
:key="index"
@click="handleClickToDetail"
>
<div class="main-item-left">
<div class="left-left">{{ item.time }}</div>
<div class="left-right">
<div class="icon"></div>
<div class="line"></div>
</div>
</div>
<div class="main-item-center">
<div class="center-header">
<div class="title">{{ item.title }}</div>
<div
class="type-box"
:class="{
type1: item.status === 1,
type2: item.status === 2,
type3: item.status === 3
}"
>
{{ item.type }}
</div>
</div>
<div class="desc">{{ item.desc }}</div>
<div class="tag-box">
<div class="tag" v-for="(val, idx) in item.tagList" :key="idx">{{ val }}</div>
</div>
</div>
<!-- <div class="main-item-left">
<div
class="left-box"
:class="{
type1: decree.status === 1,
type2: decree.status === 2,
type3: decree.status === 3
}"
>
{{ decree.type }}
</div>
</div>
<div class="main-item-center">
<div class="main-item-center-box1">{{ decree.title }}</div>
<div class="main-item-center-box2">{{ decree.time }}</div>
</div>
<div class="main-item-right">
<div class="main-item-right-text">{{ "查看官网政令原文" }}</div>
<div class="main-item-right-icon">
<img src="./assets/images/open-icon.png" alt="" />
</div>
</div> -->
</div>
</div>
<div class="footer-box">
<div class="footer-left">
{{ `共${decreeList.length}项调查` }}
</div>
<div class="footer-right">
<el-pagination
@current-change="handleCurrentChange"
:pageSize="10"
:current-page="currentPage"
background
layout="prev, pager, next"
:total="decreeList.length"
/>
</div>
</div>
</div>
</div>
......@@ -935,109 +1008,91 @@ const decreeList = ref([
type: "总统政令",
status: 1,
title: "关于进一步延长TikTok执法宽限期的行政令",
time: "2025年9月16日"
time: "2025年9月16日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "为国家安全部署先进核反应堆技术",
time: "2025年9月16日"
time: "2025年9月16日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "修改对等关税税率以反映与中华人民共和国会谈情况的行政令",
time: "2025年9月15日"
time: "2025年9月15日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "调整互惠关税范围,并制定实施贸易和安全协议的程序",
time: "2025年9月15日"
time: "2025年9月15日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "持续努力加强国家网络安全,并修订第13694号行政命令和第14144号行政命令",
time: "2025年9月14日"
time: "2025年9月14日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统备忘录",
status: 3,
title: "通过第232条款行动确保加工关键矿物及衍生产品的国家安全与经济韧性",
time: "2025年9月14日"
time: "2025年9月14日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "提名与任命",
status: 2,
title: "终止对不可靠、非受控能源的市场扭曲补贴",
time: "2025年9月11日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
time: "2025年9月11日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
time: "2025年9月6日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
time: "2025年9月6日",
img: 1,
desc: "123",
tagList: ["生物科技"]
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
},
{
type: "总统政令",
status: 1,
title: "对所有国家暂停免关税待遇",
time: "2025年9月6日"
time: "2025年9月6日",
img: 1,
desc: "123",
tagList: ["生物科技"]
}
]);
const curDecreeList = computed(() => {
......@@ -1068,7 +1123,7 @@ const releaseTimeList = ref([
value: "近五年发布"
}
]);
const categoryList = ref(["全部分类", "总统政令", "提名与任命", "总统备忘录", "声明"]);
const categoryList = ref(["全部分类", "白宫", "能源部", "商务部", "战争部", "FCC", "FDA", "NASA", "NSF", "NIH"]);
const activeCate = ref("全部分类");
const handleClickCate = cate => {
......@@ -1135,6 +1190,24 @@ const handleToPosi = id => {
}
};
const areaList = [
{ id: "人工智能", name: "人工智能" },
{ id: "集成电路", name: "集成电路" },
{ id: "通信网络", name: "通信网络" },
{ id: "量子科技", name: "量子科技" }
];
const activeAreaList = ["人工智能"];
const pubTime = ref([
{ id: "2025年", name: "2025年" },
{ id: "2024年", name: "2024年" },
{ id: "2023年", name: "2023年" },
{ id: "2022年", name: "2022年" },
{ id: "2021年", name: "2021年" },
{ id: "更早时间", name: "更早时间" }
]);
const activePubTime = ref(["2025年"]);
onMounted(async () => {
let chart1 = getBarChart(chart1Data.value.dataX, chart1Data.value.dataY);
setChart(chart1, "chart1");
......@@ -2689,7 +2762,7 @@ onMounted(async () => {
}
.home-main-footer {
margin-top: 34px;
height: 1379px;
height: 1860px;
background: rgba(248, 249, 250, 1);
overflow: hidden;
.divide4 {
......@@ -2706,13 +2779,13 @@ onMounted(async () => {
justify-content: space-between;
.btn-box {
margin-top: 10px;
width: 1000px;
width: 1200px;
display: flex;
.btn {
height: 42px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-size: 20px;
font-weight: 400;
line-height: 42px;
padding: 0 24px;
......@@ -2725,8 +2798,7 @@ onMounted(async () => {
}
}
.btnActive {
padding: 0 24px;
border-radius: 21px;
font-weight: 700;
background: rgba(20, 89, 187, 1);
color: #fff;
&:hover {
......@@ -2744,138 +2816,207 @@ onMounted(async () => {
}
.home-main-footer-main {
width: 1600px;
height: 999px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
margin: 0 auto;
box-sizing: border-box;
border-radius: 10px;
.content-header {
height: 48px;
display: flex;
border-bottom: 1px solid rgba(234, 236, 238, 1);
.icon {
width: 20px;
height: 20px;
margin-top: 14px;
margin-left: 19px;
img {
width: 100%;
height: 100%;
}
}
.title {
height: 48px;
margin-left: 21px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 48px;
}
}
.content-box {
height: 850px;
margin: 0 30px;
.main-item {
display: flex;
height: 84px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.main-item-left {
width: 105px;
.left-box {
margin-top: 17px;
height: 24px;
width: 95px;
text-align: center;
// color: rgba(22, 119, 255, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 24px;
box-sizing: border-box;
border-radius: 4px;
// border: 1px solid rgba(186, 224, 255, 1);
// background: rgba(230, 244, 255, 1);
}
.type1 {
color: rgba(22, 119, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
background: rgba(230, 244, 255, 1);
}
.type2 {
color: rgba(19, 168, 168, 1);
border: 1px solid rgba(135, 232, 222, 1);
background: rgba(230, 255, 251, 1);
}
.type3 {
color: rgba(250, 140, 22, 1);
border: 1px solid rgba(255, 213, 145, 1);
background: rgba(255, 247, 230, 1);
display: flex;
gap: 16px;
.left {
width: 300px;
height: 443px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
border-radius: 10px;
.select-box {
margin-top: 21px;
.select-box-header {
display: flex;
gap: 17px;
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
}
.main-item-center {
width: 1300px;
.main-item-center-box1 {
margin-top: 15px;
.title {
height: 24px;
color: rgba(59, 65, 75, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
.main-item-center-box2 {
margin-top: 4px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
.select-main {
margin-left: 25px;
}
.select-main1 {
width: 100px;
}
}
}
.right {
width: 1284px;
height: 1489px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2);
background: rgba(255, 255, 255, 1);
box-sizing: border-box;
border-radius: 10px;
.content-header {
height: 48px;
display: flex;
border-bottom: 1px solid rgba(234, 236, 238, 1);
.icon {
width: 23px;
height: 18px;
margin-top: 16px;
margin-left: 20px;
img {
width: 100%;
height: 100%;
}
}
.main-item-right {
.title {
height: 26px;
margin-top: 11px;
margin-left: 17px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.content-box {
height: 1367px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
overflow: hidden;
.main-item {
display: flex;
.main-item-right-text {
margin-top: 18px;
width: 128px;
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
height: 136px;
box-sizing: border-box;
padding-top: 10px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.main-item-right-icon {
margin-top: 22px;
margin-left: 9px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
.main-item-left {
display: flex;
gap: 18px;
justify-content: flex-end;
.left-left {
width: 84px;
height: 48px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: right;
}
.left-right {
.icon {
width: 24px;
height: 24px;
border-radius: 12px;
background: #ccc;
}
.line {
height: 112px;
width: 2px;
background: #ddd;
margin-left: 11px;
}
}
}
.main-item-center {
margin-left: 21px;
width: 1086px;
.center-header {
display: flex;
justify-content: space-between;
.title {
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.type-box {
height: 28px;
line-height: 28px;
padding: 0 8px;
text-align: center;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
letter-spacing: 0px;
border-radius: 4px;
}
.type1 {
color: rgba(22, 119, 255, 1);
background: rgba(230, 244, 255, 1);
}
.type2 {
color: rgba(19, 168, 168, 1);
background: rgba(230, 255, 251, 1);
}
.type3 {
color: rgba(250, 140, 22, 1);
background: rgba(255, 247, 230, 1);
}
}
.desc {
margin-top: 8px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
max-height: 48px;
overflow: hidden;
}
.tag-box {
margin-top: 9px;
display: flex;
.tag {
height: 28px;
line-height: 28px;
text-align: center;
padding: 0 8px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
letter-spacing: 0px;
}
}
}
}
}
}
.footer-box {
margin: 0 30px;
height: 32px;
margin-top: 30px;
display: flex;
justify-content: space-between;
.footer-left {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 32px;
.footer-box {
margin: 0 30px;
height: 32px;
margin-top: 30px;
display: flex;
justify-content: space-between;
.footer-left {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 32px;
}
}
}
}
......
......@@ -42,7 +42,7 @@ const getBarChart = (nameList, valueList) => {
label: {
show: true,
position: 'top',
color: 'var(--color-main-active)',
color: 'rgba(5, 95, 194, 1)',
fontWeight: 'bold', // 文字加粗
fontSize: 14,
formatter: function (params) {
......@@ -59,7 +59,7 @@ const getBarChart = (nameList, valueList) => {
},
{
offset: 1,
color: 'var(--color-main-active)'
color: 'rgba(5, 95, 194, 1)'
}
]);
},
......
......@@ -2,16 +2,16 @@
{
"id": null,
"name": "2025-10-09",
"count": 41
"count": 10
},
{
"id": null,
"name": "2025-10-08",
"count": 398
"count": 8
},
{
"id": null,
"name": "2025-09-16",
"count": 138
"count": 16
}
]
\ No newline at end of file
......@@ -47,10 +47,10 @@
<div class="item" v-for="(item, index) in subPanel4" :key="index">
<div class="name">{{ item.name }}</div>
<div class="infoWrap">
<div class="shizhi">市值:{{ item.shizhi }}</div>
<div class="address">地址:{{ item.address }}</div>
<div class="hangye">行业:{{ item.hangye }}</div>
<div class="type">类型:{{ item.type }}</div>
<div class="shizhi">{{ item.shizhi }}</div>
<div class="address">{{ item.address }}</div>
<div class="hangye">{{ item.hangye }}</div>
<div class="type">{{ item.type }}</div>
<div class="detail">查看详情</div>
</div>
</div>
......@@ -65,13 +65,16 @@
</template>
<script setup>
import { ref, shallowRef, onMounted } from "vue";
import { ref, shallowRef, onMounted, watch } from "vue";
import CardCustom from "../../components/CardCustom.vue";
import { Search } from "@element-plus/icons-vue";
import Echarts from "@/components/Chart/index.vue";
import { getBarChart, getLineChart } from "../../utils/charts";
import _ from "lodash";
import Hint from "./hint.vue";
import { getEntityFinancing } from "@/api/exportControl";
import { getEntityFinancing, getEntityMarketValue, getKeyListedEntityList, getSanStrength } from "@/api/exportControl";
import { useRoute } from "vue-router";
const route = useRoute();
const options = [
{
value: "1",
......@@ -112,6 +115,75 @@ const subPanel4 = ref([
}
]);
// 获取重点上市企业列表数据
const fetchKeyListedEntityList = async (keyword = "") => {
try {
const data = await getKeyListedEntityList(route.query.startTime, keyword);
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例:
// [{
// orgName: "Hua Ke Logistics (HK) Limited",
// orgNameZh: "华科物流(香港)有限公司",
// marketValue: 214.34982757457735,
// address: "广州",
// industryList: ["经济", "军事", "安全"]
// }]
subPanel4.value = data.map(item => {
// 优先使用中文名称,否则使用英文名称
const name = item.orgNameZh || item.orgName || "未知企业";
// 市值处理,保留两位小数并添加单位
const marketValue = item.marketValue ? `${item.marketValue.toFixed(2)}亿` : "未知";
// 地址信息
const address = item.address || "未知";
// 行业信息,取第一个行业或者默认值
const hangye = item.industryList && item.industryList.length > 0 ? item.industryList[0] : "未知行业";
// 类型信息,默认为"企业"
const type = item.orgType || "企业";
return {
name,
shizhi: `市值:${marketValue}`,
address: `地址:${address}`,
hangye: `行业:${hangye}`,
type: `类型:${type}`
};
});
}
} catch (error) {
console.error("获取重点上市企业列表数据失败:", error);
// 出错时使用默认数据
subPanel4.value = [
{
name: "中科星图有限公司",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "上市企业"
},
{
name: "中国科学院长春光学精密机械与物理研究所",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "研究院"
},
{
name: "中芯国际集成电路制造有限公司",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "上市企业"
}
];
}
};
// 获取上市企业融资变化数据
const fetchEntityFinancing = async () => {
try {
......@@ -121,9 +193,11 @@ const fetchEntityFinancing = async () => {
// 数据结构示例: [{name: "2025-10-09", count: 41}, ...]
// 按日期排序
const sortedData = data.sort((a, b) => {
return new Date(a.name) - new Date(b.name);
});
const sortedData = data
.sort((a, b) => {
return new Date(a.name) - new Date(b.name);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.name);
......@@ -144,19 +218,77 @@ const fetchEntityFinancing = async () => {
}
};
// 获取上市企业市值变化数据
const fetchEntityMarketValue = async () => {
try {
const data = await getEntityMarketValue();
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例: [{name: "2025-10-09", count: 220}, ...]
// 按日期排序
const sortedData = data
.sort((a, b) => {
return new Date(a.name) - new Date(b.name);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.name);
// 提取 y 轴数据(市值数据)
const seriesData = sortedData.map(item => item.count);
// 更新图表配置,使用指定的颜色
bar2Option.value = getBarChart(xAxisData, seriesData, ["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"], "市值变化");
}
} catch (error) {
console.error("获取上市企业市值变化数据失败:", error);
}
};
//
const fetchSanStrength = async () => {
try {
const data = await getSanStrength();
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例: [{name: "2025-10-09", count: 220}, ...]
// 按日期排序
const sortedData = data
.sort((a, b) => {
return new Date(a.name) - new Date(b.name);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.name);
// 提取 y 轴数据(市值数据)
const seriesData = sortedData.map(item => item.count);
// 更新图表配置,使用指定的颜色
bar1Option.value = getBarChart(xAxisData, seriesData, ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"], "制裁强度");
}
} catch (error) {
console.error("获取上市企业市值变化数据失败:", error);
}
};
onMounted(() => {
bar1Option.value = getBarChart(
["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
[219, 228, 129, 159, 152, 157, 78, 34, 56, 78],
["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"],
"制裁强度"
);
bar2Option.value = getBarChart(
["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
[39, 28, 49, 19, 22, 25, 78, 34, 56, 28],
["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"],
"市值变化"
);
// bar1Option.value = getBarChart(
// ["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
// [219, 228, 129, 159, 152, 157, 78, 34, 56, 78],
// ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"],
// "制裁强度"
// );
// bar2Option.value = getBarChart(
// ["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
// [39, 28, 49, 19, 22, 25, 78, 34, 56, 28],
// ["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"],
// "市值变化"
// );
// line1Option.value = getLineChart({
// xAxisData: [2013, 2023, 2024, 2015],
// seriesData: [434, 24, 453, 322],
......@@ -165,7 +297,19 @@ onMounted(() => {
// });
// 获取上市企业融资变化数据
fetchEntityFinancing();
fetchEntityMarketValue();
fetchKeyListedEntityList();
fetchSanStrength();
});
// 监听搜索关键词变化,重新获取数据
watch(
value3,
_.debounce(async newVal => {
console.log("关键词变化:", newVal);
await fetchKeyListedEntityList(newVal);
}, 300)
);
</script>
<style lang="scss" scoped>
......
......@@ -55,15 +55,41 @@ const bar2Option = shallowRef({});
const line1Option = shallowRef({});
const line2Option = shallowRef({});
// 获取历次制裁涉及领域数数据
const fetchEntitiesDomainCount = async () => {
try {
const data = await getEntitiesDomainCount();
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例: [{name: "2025-10-09", count: 4}, ...]
// 按日期排序
const sortedData = data.sort((a, b) => {
return new Date(a.name) - new Date(b.name);
});
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.name);
// 提取 y 轴数据(领域数)
const seriesData = sortedData.map(item => item.count);
// 更新图表配置,使用指定的颜色
bar2Option.value = getBarChart(xAxisData, seriesData, ["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"], "领域数");
}
} catch (error) {
console.error("获取历次制裁涉及领域数数据失败:", error);
}
};
onMounted(async () => {
try {
const [entitiesAreaCountByYearData, countThisDomainData4, entitiesDomainCountData, countThisDomainData10] =
await Promise.all([
getEntitiesAreaCountByYear(route.query.startTime),
getCountThisDomain("4"),
getEntitiesDomainCount(),
getCountThisDomain("10")
]);
const [entitiesAreaCountByYearData, countThisDomainData4, countThisDomainData10] = await Promise.all([
getEntitiesAreaCountByYear(route.query.startTime),
getCountThisDomain("4"),
// getEntitiesDomainCount(),
getCountThisDomain("10")
]);
pie1Option.value = getPieOption1(entitiesAreaCountByYearData ?? []);
const list4 = _.reverse(countThisDomainData4 ?? []);
......@@ -74,12 +100,12 @@ onMounted(async () => {
color: "rgba(255, 149, 77, 1)"
});
bar2Option.value = getBarChart(
_.reverse(entitiesDomainCountData?.xAxis ?? []),
_.reverse(entitiesDomainCountData?.series ?? []),
["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"],
"领域数"
);
// bar2Option.value = getBarChart(
// _.reverse(entitiesDomainCountData?.xAxis ?? []),
// _.reverse(entitiesDomainCountData?.series ?? []),
// ["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"],
// "领域数"
// );
const list10 = _.reverse(countThisDomainData10 ?? []);
line2Option.value = getLineChart({
......@@ -88,6 +114,7 @@ onMounted(async () => {
name: "航空航天领域",
color: "rgba(146, 84, 222, 1)"
});
await fetchEntitiesDomainCount();
} catch (err) {
console.log(err);
}
......
......@@ -66,37 +66,39 @@ const activePanelId = ref(null);
onMounted(async () => {
try {
const [compareCountSanData] = await Promise.all([getCompareCountSan(route.query.startTime)]);
const { countSanNow, countSanLast, countDomainNow, countDomainLast, countTypeNow, countTypeLast } =
compareCountSanData ?? {};
// const { countSanNow, countSanLast, countDomainNow, countDomainLast, countTypeNow, countTypeLast } =
// compareCountSanData ?? {};
const { entityNum, entityChange, listedCompanyNum, listedCompanyChange, domainNum, domainChange, typeNum, typeChange } =
compareCountSanData ?? {};
compareCountSan.value = [
{
id: 1,
name: "新增实体数量",
number: countSanNow,
isUp: countSanNow - countSanLast >= 0,
ComparisonNumber: Math.abs(countSanNow - countSanLast)
number: entityNum,
isUp: entityChange >= 0,
ComparisonNumber: Math.abs(entityChange)
},
{
id: 2,
name: "上市企业数量",
number: "--",
isUp: true,
ComparisonNumber: "--"
number: listedCompanyNum,
isUp: listedCompanyChange >= 0,
ComparisonNumber: Math.abs(listedCompanyChange)
},
{
id: 3,
name: "涉及领域数量",
number: countDomainNow,
isUp: countDomainNow - countDomainLast >= 0,
ComparisonNumber: Math.abs(countDomainNow - countDomainLast)
number: domainNum,
isUp: domainChange >= 0,
ComparisonNumber: Math.abs(domainChange)
},
{
id: 4,
name: "实体类别数量",
number: countTypeNow,
isUp: countTypeNow - countTypeLast >= 0,
ComparisonNumber: Math.abs(countTypeNow - countTypeLast)
number: typeNum,
isUp: typeChange >= 0,
ComparisonNumber: Math.abs(typeChange)
}
];
activePanelId.value = compareCountSan.value[0].id;
......
......@@ -449,7 +449,7 @@
<el-col :span="16">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right>
<div class="box5-header-right">1329家实体</div>
<div class="box5-header-right">{{ total }}家实体</div>
</template>
<template #default>
<div class="box5">
......@@ -470,7 +470,22 @@
<el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope">
<div class="tableName">{{ scope.row.name }}</div>
<div class="tableName">
<el-image
v-if="scope.row.img"
class="box1-bottom-content-item-img"
:src="scope.row.img"
alt=""
></el-image>
<div v-else class="box1-bottom-content-item-imgUndefined">
{{
(scope.row.name || scope.row.enName)?.match(
/[\u4e00-\u9fa5a-zA-Z0-9]/
)?.[0]
}}
</div>
{{ scope.row.name }}
</div>
</template>
</el-table-column>
......@@ -480,7 +495,9 @@
<el-tag
v-for="tag in scope.row.domains"
:key="tag"
:type="tag === '通信网络' ? 'primary' : 'danger'"
:type="
tag === '通信网络' ? 'primary' : tagsType[Math.floor(Math.random() * 5)]
"
>{{ tag }}</el-tag
>
</div>
......@@ -521,7 +538,6 @@
'revenue-cell',
scope.row.revenue === '无营收数据' ? 'no-revenue' : ''
]"
>
{{ scope.row.name }}...等
</div>
......@@ -622,12 +638,15 @@ import {
getCountDomainByYear,
getSanctionsInfoCount,
getEntitiesList,
getSanctionProcess
getSanctionProcess,
getSanDomainCount
} from "@/api/exportControl";
import { getMultipleBarChart_m } from "./utils/charts";
import { formatAnyDateToChinese } from "./utils";
import _ from "lodash";
const tagsType = ["primary", "success", "info", "warning", "danger"];
//数据定义
const entitiesDataInfoReactive = shallowRef({
chNum: undefined,
......@@ -693,6 +712,8 @@ onMounted(async () => {
// });
await fetchEntitiesList(currentPage.value, pageSize.value);
await fetchSanctionProcess(1, pageSize.value);
// 获取雷达图数据
await fetchRadarData();
// console.log("entitiesList entitiesList", entityBody);
// entitiesList.value = _.map(entityBody.content, item => {
// return {
......@@ -872,7 +893,7 @@ const radarOption = ref({
},
series: [
{
name: "Budget vs spending",
name: "",
type: "radar",
data: [
{
......@@ -901,6 +922,68 @@ const radarOption = ref({
]
});
// 获取雷达图数据
const fetchRadarData = async () => {
try {
const data = await getSanDomainCount();
if (data && Array.isArray(data) && data.length > 0) {
// 收集所有可能的领域名称
const allDomains = new Set();
data.forEach(item => {
if (item.domainCountInfo) {
item.domainCountInfo.forEach(domain => {
allDomains.add(domain.name);
});
}
});
const domainNames = Array.from(allDomains);
// 为每个制裁类型准备数据
const seriesData = data.map(sanItem => {
// 创建一个映射,将领域名称映射到数量
const domainMap = {};
if (sanItem.domainCountInfo) {
sanItem.domainCountInfo.forEach(domain => {
domainMap[domain.name] = domain.count;
});
}
// 按照统一的领域顺序创建值数组
const values = domainNames.map(name => domainMap[name] || 0);
// 确定颜色
let color = "rgba(10, 87, 166, 0.2)"; // 默认实体清单颜色
if (sanItem.sanTypeName === "商业管制清单") {
color = "rgba(206, 79, 81, 0.2)";
} else if (sanItem.sanTypeName === "关键和新型技术清单") {
color = "rgba(250, 140, 22, 0.2)";
}
return {
value: values,
name: sanItem.sanTypeName,
areaStyle: { color }
};
});
// 更新雷达图指标
const maxValue = Math.max(...seriesData.flatMap(item => item.value)) * 1.2;
const indicators = domainNames.map(name => ({
name: name,
max: maxValue || 100 // 防止max为0的情况
}));
// 更新雷达图配置
radarOption.value.radar.indicator = indicators;
radarOption.value.series[0].data = seriesData;
radarOption.value.legend.data = data.map(item => item.sanTypeName);
}
} catch (error) {
console.error("获取雷达图数据失败:", error);
}
};
// 进度条状态
const getStatus = _percent => {
const percent = _percent * 100;
......@@ -2464,12 +2547,27 @@ onMounted(async () => {
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
display: flex;
align-items: center;
gap: 10px;
.box1-bottom-content-item-imgUndefined {
width: 24px;
height: 24px;
font-size: 14px;
font-weight: 700;
flex-shrink: 0;
color: rgb(5, 95, 194);
background-color: rgb(236, 245, 255);
line-height: 24px;
text-align: center;
border-radius: 12px;
}
}
.num-item {
width: 280px;
display: flex;
.name-item{
.name-item {
width: 215px;
overflow: hidden;
text-overflow: ellipsis;
......
......@@ -1344,17 +1344,6 @@ const chart1Data = ref({
]
});
// 获取热门法案
const handleGetHotBills = async () => {
try {
const res = await getHotBills();
console.log("热门法案", res);
billList.value = res.data;
} catch (error) {
console.error(error);
}
};
// 根据法案类型获取法案列表
const handleGetBillsByType = async () => {
const params = {
......@@ -1376,7 +1365,6 @@ const handleGetBillsByType = async () => {
onMounted(async () => {
handleGetHylyList();
await handleGetHotBills();
curBill.value = billList.value[0];
handleGetBillsByType();
let chart1 = getMultiLineChart(chart1Data.value.title, chart1Data.value.data[0].value, chart1Data.value.data[1].value);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论