提交 467910f8 authored 作者: 闫鹏's avatar 闫鹏

合并分支 'yp-dev' 到 'pre'

Yp dev 查看合并请求 !304
流水线 #323 已通过 于阶段
in 3 分 20 秒
import request from "@/api/request.js"; // import request from "@/api/request.js";
\ No newline at end of file // import { ElMessage } from "element-plus";
// const request200 = requestP => {
// return requestP.then(data => {
// if (data.code === 200) {
// console.log('返回的数据结构 =>', data.data)
// return data.data;
// }
// ElMessage({
// message: data.message,
// type: "error",
// duration: 3 * 1000
// });
// return null;
// });
// };
// export function getDataCount() {
// return request200(
// request({
// method: "GET",
// // url: "/api/entitiesDataCount/countData",
// url: "/api/sanctionList/invFin/getTotalInfo"
// })
// );
// }
import { http } from "./service.js";
/**
* 总次数统计
*/
export function getDataCount() {
return http.get("/api/sanctionList/invFin/getTotalInfo");
}
/**
* 最新出口管制政策
* url:/sanctionList/invFin/getLatestEntityListInfo
*/
export function getLatestEntityListInfo() {
return http.get("/api/sanctionList/invFin/getLatestEntityListInfo");
}
/**
* 风险信号
* url:/commonFeature/riskSignal/{moduleId}
*/
export function getRiskSignal(moduleId='0104') {
return http.get(`/api/commonFeature/riskSignal/${moduleId}`);
}
/**
* 新闻资讯
* url:/commonFeature/news/{moduleId}
*/
export function getNewsInfo(moduleId='0104') {
return http.get(`/api/commonFeature/news/${moduleId}`);
}
/**
* 社交媒体信息
* url:/commonFeature/remarks/{moduleId}
*/
export function getSocialMediaInfo(moduleId='0104') {
return http.get(`/api/commonFeature/remarks/${moduleId}`);
}
/**
* 发布频度
* url:/entitiesDataCount/getAnnualCount
*/
export function getReleaseCount(id) {
return http.get(`/api/entitiesDataCount/getAnnualCount?sanTypeId=${id}`);
}
/**
* 制裁领域分析
* url:/entitiesDataCount/getSanDomainCount
*/
export function getSanDomainCount(sanTypeIds) {
return http.get(`/api/entitiesDataCount/getSanDomainCount?sanTypeIds=${sanTypeIds}`);
}
/**
* 制裁清单增长趋势
* url:/entitiesDataCount/getAnnualSanDomain
*/
export function getAnnualSanDomain(params) {
return http.post("/api/entitiesDataCount/getAnnualSanDomain", params);
}
/**
* 全部制裁(历史制裁过程)
* url:/entitiesDataCount/getSanctionProcess
*/
export function getSanctionProcess(sanTypeIds = "1", pageNum = 1, pageSize = 10, isCn = false) {
return http.post("/api/entitiesDataCount/getSanctionProcess",{
sanTypeIds,
// typeName: tabMap[sanTypeId],
pageNum,
pageSize,
isCn
});
}
// 引入 axios 请求
import axios from 'axios'
// 引入 element-plus 里面的消息提示
import { ElMessage } from 'element-plus'
// Token 管理
const TOKEN_KEY = 'auth_token'
// 获取token
const getToken = () => {
return 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYXRhLWNlbnRlciIsImF1ZCI6IndlYiIsImlzcyI6ImRhdGEtY2VudGVyIiwiZXhwIjozODI1ODM1NTkxLCJpYXQiOjE2NzgzNTE5NTMsImp0aSI6IjI4YmY1NTZjMTc0MDQ3YjJiNTExNWM3NzVhYjhlNWRmIiwidXNlcm5hbWUiOiJzdXBlcl91c2VyIn0.zHyVzsleX2lEqjDBYRpwluu_wy2nZKGl0dw3IUGnKNw'
// return localStorage.getItem(TOKEN_KEY)
}
// 设置 token
const setToken = (token) => {
localStorage.setItem(TOKEN_KEY, token)
}
// 移除 token
const removeToken = () => {
localStorage.removeItem(TOKEN_KEY)
}
// 导出 token 管理方法
export { getToken, setToken, removeToken }
// 创建 axios 实例
const service = axios.create({
timeout: 300 * 1000 // 请求超时时间
})
// request 拦截器
service.interceptors.request.use(config => {
// 获取 token 并添加到请求头
const token = getToken()
if (token) {
config.headers['token'] = token
}
// 图表解读等 AI 分析服务(Vite 代理 /aiAnalysis)需要 X-API-Key
const reqUrl = String(config.url || '')
if (reqUrl.includes('aiAnalysis')) {
const aiApiKey = import.meta.env.VITE_AI_ANALYSIS_API_KEY
if (aiApiKey) {
config.headers['X-API-Key'] = aiApiKey
}
}
return config
}, error => {
console.log(error)
return Promise.reject(error)
})
// response 拦截器
service.interceptors.response.use(
response => {
const res = response.data
// 根据需求:接口返回 code 不等于 200 的时候报错
if (res.code !== 200) {
ElMessage({
message: res.message || '请求失败',
type: 'error',
duration: 3 * 1000
})
return Promise.reject(res)
}
return res.data
},
error => {
console.log('err' + error)
const isCanceledError =
error?.code === 'ERR_CANCELED' ||
error?.name === 'CanceledError' ||
error?.name === 'AbortError' ||
(typeof error?.message === 'string' && /canceled/i.test(error.message))
if (isCanceledError) return Promise.reject(error)
// 处理 token 过期或无效的情况
const errUrl = String(error.config?.url || '')
const isAiAnalysisRequest = errUrl.includes('aiAnalysis')
if (
error.response &&
(error.response.status === 401 || error.response.status === 403) &&
!isAiAnalysisRequest
) {
ElMessage({
message: 'Token 已过期,请重新登录',
type: 'error',
duration: 3 * 1000
})
removeToken()
} else {
ElMessage({
message: error.message,
type: 'error',
duration: 3 * 1000
})
}
return Promise.reject(error)
}
)
// 封装通用请求函数(支持 http(config) 和 http.get/post 等调用方式)
function http(config) {
return service(config)
}
// 为 http 函数添加快捷方法
http.get = function(url, params) {
return service({ url, method: 'get', params })
}
http.post = function(url, data) {
return service({ url, method: 'post', data })
}
http.put = function(url, data) {
return service({ url, method: 'put', data })
}
http.delete = function(url, params) {
return service({ url, method: 'delete', params })
}
export { http }
export default service
\ No newline at end of file
...@@ -58,7 +58,7 @@ const routes = [ ...@@ -58,7 +58,7 @@ const routes = [
]; ];
console.log('路由', routes)
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
routes routes
......
...@@ -5,7 +5,7 @@ const financeRoutes = [ ...@@ -5,7 +5,7 @@ const financeRoutes = [
// 投融资限制 // 投融资限制
{ {
path: "/finance", path: "/finance",
name: "finance", name: "Finance",
component: Finance, component: Finance,
meta: { meta: {
title: "投融资限制概览", title: "投融资限制概览",
......
...@@ -73,13 +73,18 @@ ...@@ -73,13 +73,18 @@
</div> </div>
<div class="box1-top-content-item"> <div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 涉及领域:</span> <span class="box1-top-content-item-title">· 涉及领域:</span>
<div <!-- <div
class="box1-top-content-item-tags" class="box1-top-content-item-tags"
v-for="(domainItem, index) in item.domains" v-for="(domainItem, index) in item.domains"
:key="index" :key="index"
> >
<el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag> <el-tag :type="getTagType(domainItem)">{{ domainItem }}</el-tag>
</div> </div> -->
<AreaTag
v-for="(domainItem, index) in item.domains"
:key="index"
:tagName="domainItem"
/>
</div> </div>
</div> </div>
</div> </div>
......
{
"code": 200,
"message": "操作成功",
"success": true,
"data": {
"content": [
{
"createTime": null,
"updateTime": null,
"id": "3357",
"entityName": "Arrow Electronics (Hong Kong) Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "艾睿电子(香港)有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管制的项目,协助为伊朗代理人(包括哈马斯(Hamas))购买美国原产的电子元件,用于武器化无人机(UAV),违反美国国家安全;协助为伊朗代理人操作的武器化无人驾驶航空系统(UAS)购买美国原产的电子元件。",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [],
"ruleOrgCount": 39
},
{
"createTime": null,
"updateTime": null,
"id": "3ef6b0b3-4f6a-4",
"entityName": "Shanghai Langqing Electronic Technology Co.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "上海朗晴电子科技有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的物品",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"经济"
],
"ruleOrgCount": 93
},
{
"createTime": null,
"updateTime": null,
"id": "81fc1a85-a2be-4",
"entityName": "Easy Fly Intelligent Technology Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "易飞智能科技有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的物品",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"科技"
],
"ruleOrgCount": 18
},
{
"createTime": null,
"updateTime": null,
"id": "2813d17d-8edd-4",
"entityName": "Beijing Rageflight Technology Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "北京锐飞科技有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的物品,为伊朗代理人操作的武器化无人驾驶航空系统(UAS)采购美国原产电子组件提供了便利。",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"科技",
"军事"
],
"ruleOrgCount": 2
},
{
"createTime": null,
"updateTime": null,
"id": "f7bf81c6-9f5c-4",
"entityName": "Beijing Kevins Technology Development Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "北京凯文斯科技发展有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的项目,协助为伊朗代理人操作的武器化无人飞行系统(UAS)采购美国原产电子元件;协助为伊朗代理人,包括哈马斯(Hamas),采购用于武器化无人飞行器(UAV)的美国原产电子元件,此举违背了美国国家安全。",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"经济",
"安全"
],
"ruleOrgCount": 51
},
{
"createTime": null,
"updateTime": null,
"id": "3add08b1-3a8a-4",
"entityName": "China Address 17",
"sanTypeId": 1,
"entityType": 7,
"entityId": null,
"entityNameZh": "中国地址17",
"countryId": "0101",
"sanReason": "与艾米丽·刘(Emily Liu)的采购网络相关联,此人先前被海外资产控制办公室(OFAC)指定为支持伊朗设拉子电子工业(SEI)",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"安全"
],
"ruleOrgCount": 66
},
{
"createTime": null,
"updateTime": null,
"id": "2b660bc7-d770-4",
"entityName": "Address 18",
"sanTypeId": 1,
"entityType": 7,
"entityId": null,
"entityNameZh": "地址18",
"countryId": "0101",
"sanReason": "对于商业管制清单(CCL)上的物品以及《出口管理条例》(EAR)第746部分第7号补编中列出的EAR 99物品",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"经济"
],
"ruleOrgCount": 1
},
{
"createTime": null,
"updateTime": null,
"id": "ccaa8cd6-2b78-4",
"entityName": "Shanghai Bitconn Electronics Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "上海比通电子有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)约束的项目,为伊朗代理人操作的武器化无人驾驶航空系统(UAS)购买美国原产电子元件提供了便利。",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"经济",
"军事",
"科技"
],
"ruleOrgCount": 15
},
{
"createTime": null,
"updateTime": null,
"id": "8aa1686a-33e8-4",
"entityName": "Feng Bao Electronic Information Technology (Shanghai) Co., Ltd.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "丰宝电子信息科技(上海)有限公司",
"countryId": "0101",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的项目",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"科技"
],
"ruleOrgCount": 42
},
{
"createTime": null,
"updateTime": null,
"id": "d9016b50-dee1-4",
"entityName": "Royal Impact Trading L.L.C.",
"sanTypeId": 1,
"entityType": 2,
"entityId": null,
"entityNameZh": "皇家影响力贸易有限责任公司",
"countryId": "134",
"sanReason": "对于所有受《出口管理条例》(EAR)管辖的项目,根据EAR第744.11节的规定,推定为拒绝批准,将美国原产项目转移至伊朗,包括归类于ECCN 2B350的项目。",
"sanIntensity": "\u0000",
"startTime": "2025-10-09",
"endTime": null,
"isKey": "\u0000",
"techDomainList": null,
"techDomains": [
"经济"
],
"ruleOrgCount": 39
}
],
"pageable": {
"sort": {
"unsorted": false,
"sorted": true,
"empty": false
},
"pageNumber": 0,
"pageSize": 10,
"offset": 0,
"unpaged": false,
"paged": true
},
"totalPages": 347,
"last": false,
"totalElements": 3461,
"sort": {
"unsorted": false,
"sorted": true,
"empty": false
},
"first": true,
"numberOfElements": 10,
"size": 10,
"number": 0,
"empty": false
}
}
\ No newline at end of file
...@@ -4,74 +4,138 @@ ...@@ -4,74 +4,138 @@
<div class="header-title"> <div class="header-title">
<img :src="headerTitle.img" alt="" /> <img :src="headerTitle.img" alt="" />
<div> <div>
<div class="title"> <div class="title">{{ headerTitle.title }}</div>
{{ headerTitle.title }} <div class="department">{{ headerTitle.department }}</div>
<!-- <span>{{ headerTitle.titleEn }}</span> -->
</div> </div>
<div class="department">
{{ headerTitle.department }}
</div> </div>
</div> </div>
<!-- <div class="btn"> <div class="main">
<img :src="icon01" alt="">切换 <div class="main-header">
</div> --> <div class="header-left">实体清单制裁文件</div>
<div class="header-right">
<!-- 中英文切换开关 -->
<div class="toggle-group">
<span :class="{ active: !showChinese }">英文</span>
<el-switch
v-model="showChinese"
active-text="中"
inactive-text="英"
:inline-prompt="true"
@change="handleToggleChange"
/>
<span :class="{ active: showChinese }">中文</span>
</div> </div>
<!-- 下载按钮 -->
<el-button type="primary" :icon="Download" @click="handleDownload"> 下载 </el-button>
</div> </div>
<div class="main">
<div class="pdf-container">
<iframe v-if="headerTitle.srcUrl" :src="headerTitle.srcUrl" width="100%" height="100%" frameborder="0"></iframe>
<div v-else class="no-pdf">暂无原文</div>
</div> </div>
<div class="pdf-container"> <!-- 外层滚动容器,统一控制两侧滚动 -->
<iframe <div class="report-box" ref="reportBoxRef">
v-if="headerTitle.transUrl" <div class="pdf-pane-wrap" :class="{ 'center-mode': !showChinese }">
:src="headerTitle.transUrl" <pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" />
width="100%" </div>
height="100%" <div class="pdf-pane-wrap" v-if="showChinese">
frameborder="0" <pdf ref="rightPdfRef" :pdfUrl="headerTitle.transUrl" class="pdf-pane-inner" />
></iframe> </div>
<div v-else class="no-pdf">暂无译文</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, onMounted, watch, computed } from "vue";
import { Download } from "@element-plus/icons-vue";
import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js"; import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js";
import title from "../assets/title.png"; import title from "../assets/title.png";
import icon01 from "../assets/icon01.png"; import pdf from "./pdf.vue";
const leftPdfRef = ref(null);
const rightPdfRef = ref(null);
const reportBoxRef = ref(null);
const headerTitle = ref({
img: title,
title: "",
department: "",
srcUrl: "",
transUrl: ""
});
const sanRecordId = ref("");
const isSyncing = ref(false);
// ✅ 控制中文 PDF 显示
const showChinese = ref(true);
// ✅ 计算当前显示模式
const showMode = computed(() => {
return showChinese.value ? "both" : "en";
});
// ✅ 切换中英文显示
const handleToggleChange = value => {
console.log("切换中英文显示:", value ? "中英双栏" : "仅英文");
showChinese.value = value;
};
// ✅ 下载功能
const handleDownload = async () => {
const files = [
{ url: headerTitle.value.srcUrl, name: "英文原版.pdf" },
{ url: headerTitle.value.transUrl, name: "中文翻译.pdf" }
];
for (const file of files) {
if (file.url) {
try {
const response = await fetch(file.url);
const blob = await response.blob();
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} catch (error) {
console.error(`下载${file.name}失败:`, error);
}
}
}
};
const getUrlParams = () => {
const urlParams = new URLSearchParams(window.location.search);
sanRecordId.value = urlParams.get("id") || "";
};
// 单次制裁-制裁概况-基本信息
const singleSanctionOverview = ref({});
const getSingleSanctionOverviewData = async () => { const getSingleSanctionOverviewData = async () => {
if (!sanRecordId.value) return; if (!sanRecordId.value) return;
try { try {
const res = await getSingleSanctionOverview({ const res = await getSingleSanctionOverview({
sanRecordId: sanRecordId.value sanRecordId: sanRecordId.value
}); });
if (res.code === 200) { if (res.code === 200 && res.data) {
singleSanctionOverview.value = res.data || {}; const singleSanctionOverview = res.data || {};
// 格式化日期 // 格式化日期
let dateStr = ""; let dateStr = "";
if (singleSanctionOverview.value.postDate) { if (singleSanctionOverview.postDate) {
const date = new Date(singleSanctionOverview.value.postDate); const date = new Date(singleSanctionOverview.postDate);
if (!isNaN(date.getTime())) { if (!isNaN(date.getTime())) {
dateStr = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日`; dateStr = `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日`;
} else { } else {
dateStr = singleSanctionOverview.value.postDate; dateStr = singleSanctionOverview.postDate;
} }
} }
// 更新头部信息 // 更新头部信息
headerTitle.value = { headerTitle.value = {
...headerTitle.value, ...headerTitle.value,
title: `${dateStr}${singleSanctionOverview.value.sanTitleZh || singleSanctionOverview.value.sanTitle}》`, title: `${dateStr}${singleSanctionOverview.sanTitleZh || singleSanctionOverview.sanTitle}》`,
titleEn: singleSanctionOverview.value.sanTitle || "", department: singleSanctionOverview.fileCode || "",
department: singleSanctionOverview.value.fileCode || "", srcUrl: singleSanctionOverview.srcUrl || "",
srcUrl: singleSanctionOverview.value.srcUrl || "", transUrl: singleSanctionOverview.transUrl || ""
transUrl: singleSanctionOverview.value.transUrl || ""
}; };
} }
} catch (error) { } catch (error) {
...@@ -79,34 +143,64 @@ const getSingleSanctionOverviewData = async () => { ...@@ -79,34 +143,64 @@ const getSingleSanctionOverviewData = async () => {
} }
}; };
const headerTitle = ref({ // 同步滚动处理
img: title const handleSyncScroll = () => {
}); if (isSyncing.value) return;
// 获取URL参数 isSyncing.value = true;
const sanRecordId = ref("");
const getUrlParams = () => { requestAnimationFrame(() => {
const urlParams = new URLSearchParams(window.location.search); isSyncing.value = false;
sanRecordId.value = urlParams.get("id") || ""; });
};
// 监听滚动事件
const setupScrollSync = () => {
const reportBox = reportBoxRef.value;
if (!reportBox) return;
reportBox.addEventListener("scroll", handleSyncScroll, { passive: true });
}; };
// 监听 PDF 加载完成
watch(
() => [headerTitle.value.srcUrl, headerTitle.value.transUrl],
() => {
setTimeout(() => {
setupScrollSync();
}, 1000);
},
{ deep: true }
);
onMounted(() => { onMounted(() => {
getUrlParams(); getUrlParams();
getSingleSanctionOverviewData(); getSingleSanctionOverviewData();
setTimeout(() => {
setupScrollSync();
}, 500);
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* { // * {
margin: 0; // margin: 0;
padding: 0; // padding: 0;
} // }
.entity-list { .entity-list {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto;
.header { .header {
width: 100%; width: 100%;
height: 148px; height: 148px;
background-color: #fff; background-color: #fff;
padding-top: 16px; padding-top: 16px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
.header-title { .header-title {
width: 1601px; width: 1601px;
height: 72px; height: 72px;
...@@ -118,27 +212,22 @@ onMounted(() => { ...@@ -118,27 +212,22 @@ onMounted(() => {
align-items: center; align-items: center;
margin-bottom: 12px; margin-bottom: 12px;
position: relative; position: relative;
img { img {
width: 54px; width: 54px;
height: 54px; height: 54px;
margin-left: 15px; margin-left: 15px;
margin-right: 11px; margin-right: 11px;
} }
.title { .title {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 26px; line-height: 26px;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
span {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
margin-left: 11px;
}
} }
.department { .department {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
...@@ -146,125 +235,108 @@ onMounted(() => { ...@@ -146,125 +235,108 @@ onMounted(() => {
line-height: 24px; line-height: 24px;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
} }
.btn {
cursor: pointer;
display: flex;
align-items: center;
position: absolute;
right: 16px;
top: 25px;
font-size: 18px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(5, 95, 194);
img {
width: 20px;
height: 20px;
margin-right: 7px;
}
} }
} }
.header-nav {
width: 1601px; .main {
margin: 0 auto; margin: 0 auto;
height: 48px; background: rgb(255, 255, 255);
width: 1601px;
height: calc(100vh - 148px);
margin-bottom: 20px;
border: 1px solid rgb(234, 236, 238);
box-shadow: 0 0 20px 0 rgba(25, 69, 130, 0.1);
.main-header {
height: 64px;
border-bottom: 1px solid rgb(234, 236, 238);
background: rgb(255, 255, 255);
margin: 0 70px;
color: rgba(59, 65, 75, 1);
font-family: "Source Han Sans CN";
font-size: 20px;
font-weight: 700;
line-height: 26px;
width: 1456px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
.nav-item { .header-right {
display: flex; display: flex;
align-items: center; align-items: center;
height: 100%; gap: 24px;
margin-right: 32px;
cursor: pointer;
position: relative;
font-size: 18px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
&:last-child { .toggle-group {
margin-right: 0; display: flex;
} align-items: center;
gap: 10px;
img { span {
width: 16px; font-size: 14px;
height: 16px; color: rgb(150, 150, 150);
margin-right: 4px; transition: color 0.3s;
}
&.active { &.active {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
font-weight: 700; font-weight: 600;
}
.active-line {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 3px;
background-color: #055fc2;
border-radius: 1.5px;
} }
} }
.original-text-btn { :deep(.el-switch) {
margin-left: auto; --el-switch-on-color: #055fc2;
width: 152px; --el-switch-off-color: #e6e7e8;
height: 36px;
background: #ffffff;
border-radius: 4px;
border: 1px solid rgba(230, 231, 232, 1);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
img { .el-switch__label {
width: 16px; color: #fff;
height: 16px; font-size: 12px;
margin-right: 8px; font-weight: 600;
}
span { &.is-active {
font-size: 16px; color: #fff;
font-weight: 400;
color: rgb(95, 101, 108);
font-family: "Microsoft YaHei";
line-height: 24px;
} }
} }
} }
} }
.main {
width: 1601px;
height: calc(100% - 148px);
background-color: #f7f8f9;
margin: 0 auto;
display: flex;
justify-content: space-between;
padding-top: 20px;
box-sizing: border-box;
.pdf-container { :deep(.el-button) {
width: 790px; --el-button-bg-color: #055fc2;
height: calc(100% - 20px); --el-button-border-color: #055fc2;
background-color: #fff; --el-button-hover-bg-color: #044c9b;
// border: 1px solid rgba(174, 214, 255, 1); --el-button-hover-border-color: #044c9b;
border-radius: 4px;
overflow: hidden;
.no-pdf { font-size: 14px;
padding: 10px 20px;
}
}
}
.report-box {
margin-left: 70px;
width: 1456px;
height: calc(100% - 64px);
display: flex; display: flex;
align-items: center; overflow-y: auto;
justify-content: center; overflow-x: hidden;
}
.pdf-pane-wrap {
flex: 0 0 50%;
max-width: 50%;
height: 100%; height: 100%;
color: #909399; min-width: 0;
font-size: 16px; transition: all 0.3s;
background-color: #fff;
&.center-mode {
flex: 0 0 100%;
max-width: 100%;
margin: 0 auto;
}
} }
.pdf-pane-inner {
width: 100%;
height: 100%;
} }
} }
} }
......
...@@ -4,22 +4,29 @@ ...@@ -4,22 +4,29 @@
<div class="header-title"> <div class="header-title">
<img :src="headerTitle.img" alt="" /> <img :src="headerTitle.img" alt="" />
<div> <div>
<div class="title"> <div class="title">{{ headerTitle.title }}</div>
{{ headerTitle.title }} <div class="department">{{ headerTitle.department }}</div>
</div>
<div class="department">
{{ headerTitle.department }}
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div class="main-header"> <div class="main-header">
<div>实体清单制裁文件</div> <div class="header-left">实体清单制裁文件</div>
<div class="header-right">
<!-- 中英文切换开关 -->
<div class="toggle-group">
<!-- <span :class="{ active: !showChinese }">英文</span> -->
<el-switch v-model="showChinese" @change="handleToggleChange" />
<img :src="transIcon" alt="" />
<span :class="{ active: showChinese }">显示原文</span>
</div>
<!-- 下载按钮 -->
<el-button plain :icon="Download" @click="handleDownload"> 下载 </el-button>
</div>
</div> </div>
<!-- 外层滚动容器,统一控制两侧滚动 --> <!-- 外层滚动容器,统一控制两侧滚动 -->
<div class="report-box" ref="reportBoxRef"> <div class="report-box" ref="reportBoxRef">
<div class="pdf-pane-wrap"> <div class="pdf-pane-wrap" v-if="showChinese" :class="{ 'center-mode': !showChinese }">
<pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" /> <pdf ref="leftPdfRef" :pdfUrl="headerTitle.srcUrl" class="pdf-pane-inner" />
</div> </div>
<div class="pdf-pane-wrap"> <div class="pdf-pane-wrap">
...@@ -31,9 +38,11 @@ ...@@ -31,9 +38,11 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from "vue"; import { ref, onMounted, watch, computed } from "vue";
import { Download } from "@element-plus/icons-vue";
import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js"; import { getSingleSanctionOverview } from "@/api/exportControlV2.0.js";
import title from "../assets/title.png"; import title from "../assets/title.png";
import transIcon from "../assets/icon-translation.png";
import pdf from "./pdf.vue"; import pdf from "./pdf.vue";
const leftPdfRef = ref(null); const leftPdfRef = ref(null);
...@@ -51,6 +60,46 @@ const headerTitle = ref({ ...@@ -51,6 +60,46 @@ const headerTitle = ref({
const sanRecordId = ref(""); const sanRecordId = ref("");
const isSyncing = ref(false); const isSyncing = ref(false);
// ✅ 控制中文 PDF 显示
const showChinese = ref(true);
// ✅ 计算当前显示模式
const showMode = computed(() => {
return showChinese.value ? "both" : "en";
});
// ✅ 切换中英文显示
const handleToggleChange = value => {
console.log("切换中英文显示:", value ? "中英双栏" : "仅英文");
showChinese.value = value;
};
// ✅ 下载功能
const handleDownload = async () => {
const files = [
{ url: headerTitle.value.srcUrl, name: "英文原版.pdf" },
{ url: headerTitle.value.transUrl, name: "中文翻译.pdf" }
];
for (const file of files) {
if (file.url) {
try {
const response = await fetch(file.url);
const blob = await response.blob();
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = file.name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href);
} catch (error) {
console.error(`下载${file.name}失败:`, error);
}
}
}
};
const getUrlParams = () => { const getUrlParams = () => {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
sanRecordId.value = urlParams.get("id") || ""; sanRecordId.value = urlParams.get("id") || "";
...@@ -112,7 +161,6 @@ const setupScrollSync = () => { ...@@ -112,7 +161,6 @@ const setupScrollSync = () => {
watch( watch(
() => [headerTitle.value.srcUrl, headerTitle.value.transUrl], () => [headerTitle.value.srcUrl, headerTitle.value.transUrl],
() => { () => {
// PDF URL 变化时,等待渲染完成后设置滚动监听
setTimeout(() => { setTimeout(() => {
setupScrollSync(); setupScrollSync();
}, 1000); }, 1000);
...@@ -124,18 +172,17 @@ onMounted(() => { ...@@ -124,18 +172,17 @@ onMounted(() => {
getUrlParams(); getUrlParams();
getSingleSanctionOverviewData(); getSingleSanctionOverviewData();
// 等待 DOM 渲染完成后设置滚动监听
setTimeout(() => { setTimeout(() => {
setupScrollSync(); setupScrollSync();
}, 500); }, 500);
}); });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
* { // * {
margin: 0; // margin: 0;
padding: 0; // padding: 0;
} // }
.entity-list { .entity-list {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -207,9 +254,57 @@ onMounted(() => { ...@@ -207,9 +254,57 @@ onMounted(() => {
font-weight: 700; font-weight: 700;
line-height: 26px; line-height: 26px;
width: 1456px; width: 1456px;
text-align: left;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
.header-right {
display: flex;
align-items: center;
gap: 24px;
.toggle-group {
display: flex;
align-items: center;
gap: 10px;
span {
font-size: 14px;
color: rgb(150, 150, 150);
transition: color 0.3s;
&.active {
color: rgb(5, 95, 194);
font-weight: 600;
}
}
:deep(.el-switch) {
--el-switch-on-color: #055fc2;
--el-switch-off-color: #e6e7e8;
.el-switch__label {
color: #fff;
font-size: 12px;
font-weight: 600;
&.is-active {
color: #fff;
}
}
}
}
// :deep(.el-button) {
// --el-button-bg-color: #055fc2;
// --el-button-border-color: #055fc2;
// --el-button-hover-bg-color: #044c9b;
// --el-button-hover-border-color: #044c9b;
// font-size: 14px;
// padding: 10px 20px;
// }
}
} }
.report-box { .report-box {
...@@ -217,8 +312,10 @@ onMounted(() => { ...@@ -217,8 +312,10 @@ onMounted(() => {
width: 1456px; width: 1456px;
height: calc(100% - 64px); height: calc(100% - 64px);
display: flex; display: flex;
overflow-y: auto; /* 统一滚动条,控制两侧一起滚动 */ overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
// ✅ 添加居中对齐
justify-content: center;
} }
.pdf-pane-wrap { .pdf-pane-wrap {
...@@ -226,6 +323,15 @@ onMounted(() => { ...@@ -226,6 +323,15 @@ onMounted(() => {
max-width: 50%; max-width: 50%;
height: 100%; height: 100%;
min-width: 0; min-width: 0;
transition: all 0.3s;
&.center-mode {
flex: 0 0 100%;
max-width: 100%;
// ✅ 添加居中样式
width: 728px; // 约一半宽度,保持单栏时美观
margin: 0 auto;
}
} }
.pdf-pane-inner { .pdf-pane-inner {
......
<template>
<div class="depth-mine-container">
<div class="depth-mine-left">
<div
@click="handleClick(index)"
:class="['depth-mine-left-item', activeLeftTab === index ? 'depth-mine-left-item-active' : '']"
v-for="(item, index) in data"
:key="index"
>
<div class="depth-mine-left-item-title">{{ item[0] }}</div>
<div class="depth-mine-left-item-desc">{{ item[1] }}</div>
</div>
</div>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="实体清单历次更新中外受影响情况对比" height="580px">
<!-- 顶部右侧自定义内容 -->
<!-- <template #header-right>
<div style="display: flex; gap: 8px">
<el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button>
</div>
</template> -->
<template #default>
<!-- <div ref="echartsRef" style="height: 425px; width: 100%"></div> -->
<ECharts :option="chartOption" autoresize :style="{ height: '425px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
2024年以来,实体清单更新次数相对于2023年有所降低,但是本年度内呈上升趋势。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="实体清单历次更新中外受影响情况对比" height="580px">
<!-- 顶部右侧自定义内容 -->
<!-- <template #header-right>
<div style="display: flex; gap: 8px">
<el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button>
</div>
</template> -->
<template #default>
<div class="content-box">
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column type="index" label="序号" />
<el-table-column prop="date" label="更新日期" width="150" />
<el-table-column prop="entity" label="实体数/历史排名" width="150" />
<el-table-column prop="entityCN" label="中国实体数/历史排名" />
<el-table-column prop="proportion" label="中国占比/历史排名" />
</el-table>
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text"
>相对于近几次更新,中国新增受制裁的实体数量占比的排名都显著提高,美国制裁中国实体的强度显著提升。</span
>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="实体清单更新频率" height="505px">
<!-- 顶部右侧自定义内容 -->
<!-- <template #header-right>
<div style="display: flex; gap: 8px">
<el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button>
</div>
</template> -->
<template #default>
<div class="content-box">
<!-- <v-charts :option="option" autoresize :style="{ height: '326px' }" /> -->
<!-- <div ref="frequenceRef" style="height: 340px; width: 100%"></div> -->
<ECharts :option="frequenceOption" autoresize :style="{ height: '326px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
2024年以来,实体清单更新次数相对于2023年有所降低,但是本年度内呈上升趋势。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="重点实体列表" height="505px">
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 8px">
<el-input
placeholder="请输入关键词,使用(、)作为分隔符."
:suffix-icon="Search"
clearable
style="width: 360px"
>
<!-- <template #append>
<el-icon><Search /></el-icon>
</template> -->
</el-input>
<!-- <el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button> -->
</div>
</template>
<template #default>
<div class="content-box">
<div class="content-box-list">
<div class="content-box-list-item" v-for="item in entityList" :key="item.entity">
<div class="content-box-list-item-title">{{ item.entity }}</div>
<div class="content-box-list-item-tag">
<el-tag
:type="TAGTYPE[Math.floor(Math.random() * 5)]"
v-for="tag in item.tags"
:key="tag"
>{{ tag }}</el-tag
>
</div>
</div>
</div>
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text"
>本次实体清单更新,科研院所占比相对较高。需要注意的是,中国科学技术大学、中国科学院物理研究所等国内重点科研机构被列入实体清单,此外还有中科星图以及中电科旗下的多家研究所被列入清单,可能会对相关行业产生显著影响。</span
>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
// import * as echarts from "echarts";
import ECharts from "@/components/Chart/index.vue";
// import VCharts from "vue-echarts";
import { useEcharts } from "@/hooks/useEcharts";
// import echarts from "@/plugins/echarts";
import { Search } from "@element-plus/icons-vue";
import CustomContainer from "@/components/Container/index.vue";
import { TAGTYPE } from "@/common/constant.js";
const tableData = ref([
{
date: "2024年5月14日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2024年4月11日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2024年2月27日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2023年12月7日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2023年11月15日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: " 2024年3月5日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
}
]);
// 重点实体列表
const entityList = ref([
{
entity: "中国科学技术大学",
tags: ["双一流", "985", "关税政策"]
},
{
entity: "合肥量子信息科学国家实验室",
tags: ["中国上市企业"]
},
{
entity: "中国科学院物理研究所",
tags: ["科研院所"]
}
]);
const frequenceRef = ref(null);
const echartsRef = ref(null);
const frequenceOption = ref({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
top: 20,
right: 3,
bottom: 0,
left: 3,
containLabel: true
},
legend: {
data: [],
bottom: 0,
icon: "circle",
itemWidth: 10,
itemHeight: 10,
itemGap: 15,
textStyle: {
fontSize: 12,
color: "#222B45"
}
},
xAxis: {
type: "category",
data: [
"202301",
"202302",
"202303",
"202304",
"202305",
"202306",
"202307",
"202308",
"202309",
"202310",
"202311",
"202312"
],
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: "#889fcc"
}
},
yAxis: {
type: "value",
axisLine: { show: false },
axisTick: { show: false },
splitLine: {
lineStyle: {
color: "#EFF1F3",
width: 0.8
}
},
axisLabel: { color: "#889fcc" }
},
series: [
{
name: "aa",
type: "bar",
data: [3, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 2],
barWidth: "15",
itemStyle: {
color: "#639dfa",
borderRadius: [4, 4, 0, 0]
}
}
// {
// name: "bbb",
// type: "bar",
// data: [6, 13, 10, 5, 12, 5],
// barWidth: "15",
// itemStyle: {
// color: "#639dfa",
// borderRadius: [4, 4, 0, 0]
// }
// }
]
});
const chartOption = ref({
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
crossStyle: {
color: "#999"
}
}
},
toolbox: {
feature: {
dataView: { show: true, readOnly: false },
magicType: { show: true, type: ["line", "bar"] },
restore: { show: false },
saveAsImage: { show: false }
}
},
legend: {
data: ["中国", "外国", "占比"]
},
xAxis: [
{
type: "category",
data: ["2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021"],
axisPointer: {
type: "shadow"
}
}
],
yAxis: [
{
type: "value",
name: "量",
min: 0,
max: 250,
interval: 50,
axisLabel: {
formatter: "{value}"
}
},
{
type: "value",
name: "占比",
min: 0,
max: 25,
interval: 5,
axisLabel: {
formatter: "{value}"
}
}
],
series: [
{
name: "中国",
type: "bar",
tooltip: {
valueFormatter: function (value) {
return value;
}
},
data: [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
},
{
name: "外国",
type: "bar",
tooltip: {
valueFormatter: function (value) {
return value + " ml";
}
},
data: [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
},
{
name: "占比",
type: "line",
yAxisIndex: 1,
tooltip: {
valueFormatter: function (value) {
return value;
}
},
data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2]
}
]
});
onMounted(() => {
// let frequenceChart = echarts.init(frequenceRef.value);
// let chart = echarts.init(echartsRef.value);
// useEcharts(frequenceChart, frequenceOption);
// useEcharts(chart, chartOption);
});
const data = ref([
["新增实体37个", "较上次多31个"],
["上市企业3家", "较上次多2家"],
["涉及领域2个", "较上次少1个"],
["实体类别3种", "较上次多2种"]
]);
const activeLeftTab = ref(0);
const handleClick = index => {
activeLeftTab.value = index;
};
</script>
<style scoped>
.depth-mine-container {
padding: 0 5% 0 150px;
position: relative;
}
.depth-mine-left {
position: absolute;
left: 0;
top: 10px;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.depth-mine-left-item {
display: flex;
flex-direction: column;
gap: 4px;
cursor: pointer;
color: rgba(95, 101, 108, 1);
padding: 10px 16px;
border-radius: 10px;
}
.depth-mine-left-item-active {
color: #fff;
background-color: var(--color-main-active);
}
.depth-mine-left-item-title {
font-size: 16px;
font-weight: 400;
}
.content-box {
/* height: 90vh; */
display: flex;
flex-direction: column;
justify-content: space-between;
}
.content-box-list {
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
}
.content-box-list-item {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
padding-bottom: 5px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-box-list-item-title {
font-size: 16px;
font-weight: 700;
color: rgba(10, 18, 30, 1);
}
.content-box-list-item-tag {
display: flex;
gap: 8px;
}
.content-box-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background-color: rgba(246, 251, 255, 1);
padding: 5px 15px;
}
.content-box-footer-text {
font-size: 16px;
font-weight: 400;
color: var(--color-main-active);
}
.footer-img-small {
width: 20px;
height: 20px;
}
.footer-img-large {
width: 24px;
height: 24px;
}
</style>
<template>
<el-row :gutter="20">
<el-col :span="6">
<custom-container title="新增科研机构列表" height="1040px">
<template #header-top>
<div class="content-top">
<el-radio-group v-model="impactType" size="small">
<el-radio-button label="产业影响" value="cy" />
<el-radio-button label="科研机构影响" value="ky" />
</el-radio-group>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 8px">
<!-- <el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button> -->
<el-select v-model="orgType" :options="orgTypeOptions" :props="props" placeholder="Select" style="width: 80px" />
</div>
</template>
<template #default>
<div class="content-box-header">
<div class="content-box-header-title">名称</div>
<div class="content-box-header-title">进口仪器数</div>
</div>
<div class="content-box-centent-list">
<div class="content-box-list-item" v-for="item in entityList" :key="item.entity">
<div class="content-box-list-item-title">{{ item.entity }}</div>
<div class="content-box-list-item-num">{{ item.num }}</div>
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="18">
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="各类别仪器对美依赖情况" height="500px">
<template #default>
<div class="content-box">
<!-- <div ref="barRef" style="height: 340px; width: 100%"></div> -->
<ECharts :option="barOption" autoresize :style="{ height: '340px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
受制裁实体中,医学科研仪器对美依赖程度最高,达到50%。此外,电子策略仪器、物理性能测试仪器、激光器、核仪器、计量仪器、分析仪器等也有较高的对美依赖度,本次制裁可能会对相关企业产生较大的影响。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="仪器进口国可替代性分析" height="500px">
<template #default>
<div class="content-box">
<!-- <div ref="pieRef" style="height: 340px; width: 100%"></div> -->
<ECharts :option="pieOption" autoresize :style="{ height: '340px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
近些年,受制裁实体的国产仪器数量整体上呈增加趋势,但是2023年,各类国产仪器的新增数量均有显著降低,需要加以关注。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="仪器对美依赖度升高风险分析" height="500px">
<template #default>
<div class="content-box">
<ECharts :option="frequenceOption" autoresize :style="{ height: '340px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
近些年,受制裁实体的国产仪器数量整体上呈增加趋势,但是2023年,各类国产仪器的新增数量均有显著降低,需要加以关注。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="仪器国产化降低风险分析" height="500px">
<template #default>
<div class="content-box">
<ECharts :option="chartOption" autoresize :style="{ height: '340px' }" />
<div class="content-box-footer">
<img class="footer-img-small" src="@/assets/images/icon-guanzhu.png" alt="" />
<span class="content-box-footer-text">
近些年,受制裁实体的国产仪器数量整体上呈增加趋势,但是2023年,各类国产仪器的新增数量均有显著降低,需要加以关注。
</span>
<img class="footer-img-large" src="@/assets/images/icon-right-circle.png" alt="" />
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</el-col>
</el-row>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
// import * as echarts from "echarts";
import ECharts from "@/components/Chart/index.vue";
import { useEcharts } from "@/hooks/useEcharts";
import echarts from "@/plugins/echarts";
import { Search } from "@element-plus/icons-vue";
import CustomContainer from "@/components/Container/index.vue";
import { TAGTYPE } from "@/common/constant.js";
const impactType = ref("ky");
const orgTypeOptions = ref([
{
value: "1",
label: "对外依赖"
}
]);
const orgType = ref("1");
const tableData = ref([
{
date: "2024年5月14日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2024年4月11日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2024年2月27日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2023年12月7日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: "2023年11月15日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
},
{
date: " 2024年3月5日",
entity: "37/24",
entityCN: "37/4",
proportion: "100%/1"
}
]);
// 重点实体列表
const entityList = ref([
{
img: "",
num: 1556,
entity: "中国科学技术大学",
tags: ["双一流", "985", "关税政策"]
},
{
img: "",
num: 313,
entity: "合肥量子信息科学国家实验室",
tags: ["中国上市企业"]
},
{
img: "",
num: 270,
entity: "中国科学院物理研究所",
tags: ["科研院所"]
}
]);
const frequenceRef = ref(null);
const echartsRef = ref(null);
const pieRef = ref(null);
const barRef = ref(null);
const barOption = ref({
title: {
text: ""
},
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
legend: {},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true
},
xAxis: {
type: "value",
boundaryGap: [0, 0.01]
},
yAxis: {
type: "category",
data: ["医学科学仪器", "电子策略仪器", "物理性能测试仪器", "激光器", "核仪器", "计量仪器", "分析仪器"]
},
series: [
{
name: "2011",
type: "bar",
data: [50, 47.8, 46.9, 45.0, 40.8, 35.0, 34.8]
}
]
});
const pieOption = {
tooltip: {
trigger: "item"
},
legend: {
top: "5%",
left: "center"
},
series: [
{
name: "Access From",
type: "pie",
radius: ["40%", "70%"],
avoidLabelOverlap: false,
label: {
show: false,
position: "center"
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: "bold"
}
},
labelLine: {
show: false
},
data: [
{ value: 1048, name: "Search Engine" },
{ value: 735, name: "Direct" },
{ value: 580, name: "Email" },
{ value: 484, name: "Union Ads" },
{ value: 300, name: "Video Ads" }
]
}
]
};
const frequenceOption = {
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
top: 20,
right: 3,
bottom: 0,
left: 3,
containLabel: true
},
legend: {
data: [],
bottom: 0,
icon: "circle",
itemWidth: 10,
itemHeight: 10,
itemGap: 15,
textStyle: {
fontSize: 12,
color: "#222B45"
}
},
xAxis: {
type: "category",
data: [
"202301",
"202302",
"202303",
"202304",
"202305",
"202306",
"202307",
"202308",
"202309",
"202310",
"202311",
"202312"
],
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: "#889fcc"
}
},
yAxis: {
type: "value",
axisLine: { show: false },
axisTick: { show: false },
splitLine: {
lineStyle: {
color: "#EFF1F3",
width: 0.8
}
},
axisLabel: { color: "#889fcc" }
},
series: [
{
name: "aa",
type: "bar",
data: [3, 2, 3, 0, 2, 1, 3, 1, 0, 2, 3, 2],
barWidth: "15",
itemStyle: {
color: "#639dfa",
borderRadius: [4, 4, 0, 0]
}
}
// {
// name: "bbb",
// type: "bar",
// data: [6, 13, 10, 5, 12, 5],
// barWidth: "15",
// itemStyle: {
// color: "#639dfa",
// borderRadius: [4, 4, 0, 0]
// }
// }
]
};
const chartOption = {
title: {
text: ""
},
tooltip: {
trigger: "axis"
},
legend: {
data: ["Email", "Union Ads", "Video Ads", "Direct", "Search Engine"]
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: "category",
boundaryGap: false,
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
},
yAxis: {
type: "value"
},
series: [
{
name: "Email",
type: "line",
stack: "Total",
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: "Union Ads",
type: "line",
stack: "Total",
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: "Video Ads",
type: "line",
stack: "Total",
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: "Direct",
type: "line",
stack: "Total",
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: "Search Engine",
type: "line",
stack: "Total",
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
};
onMounted(() => {
// let frequenceChart = echarts.init(frequenceRef.value);
// let chart = echarts.init(echartsRef.value);
// let pie = echarts.init(pieRef.value);
// let bar = echarts.init(barRef.value);
// useEcharts(frequenceChart, frequenceOption);
// useEcharts(chart, chartOption);
// useEcharts(pie, pieOption);
// useEcharts(bar, barOption);
});
</script>
<style scoped>
.content-top {
width: 100%;
display: flex;
justify-content: center;
margin-top: 20px;
}
.content-box {
/* height: 90vh; */
display: flex;
flex-direction: column;
justify-content: space-between;
}
.content-box-header {
height: 40px;
background-color: rgba(59, 65, 75, 1);
color: #fff;
font-size: 16px;
font-weight: 700;
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 20px;
padding-right: 20px;
}
.content-box-list {
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
}
.content-box-list-item {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
padding-bottom: 5px;
padding-left: 10px;
padding-right: 20px;
height: 35px;
line-height: 35px;
}
.content-box-list-item-title {
font-size: 16px;
/* font-weight: 700; */
color: rgba(10, 18, 30, 1);
}
.content-box-list-item-tag {
display: flex;
gap: 8px;
}
.content-box-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background-color: rgba(246, 251, 255, 1);
padding: 5px 15px;
}
.content-box-footer-text {
font-size: 16px;
font-weight: 400;
color: var(--color-main-active);
}
.footer-img-small {
width: 20px;
height: 20px;
}
.footer-img-large {
width: 24px;
height: 24px;
}
</style>
<template>
<div style="padding: 0 5%">
<el-row :gutter="20">
<el-col :span="13">
<custom-container title="实体清单发布机构">
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 8px">
<!-- <el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input> -->
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-card">
<el-image class="content-img" :src="BISlogo" fit="fill" />
<div class="content-desc">
<div class="content-desc-item">
<div class="content-desc-title">机构名称:</div>
<div class="content-desc-content">美国商务部工业与安全局</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">相关措施:</div>
<div class="content-desc-content">出口管制条例(EAR)、实体清单、商业管制清单(CCL)、232调查等</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">法案类别:</div>
<div class="content-desc-content">公法(编号:Pub. L. No. 119-21)</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">机构职责:</div>
<div class="content-desc-content content-desc-content-long">
美国商务部工业与安全局是美国商务部的一个部门,主要负责管理和监督美国的出口管制和安全问题。通过确保有效的出口管制和条约合规体系,促进美国保持战略技术领导地位,从而推动美国国家安全、外教政策及经济目标的实现。
</div>
</div>
</div>
</div>
</template>
</custom-container>
<custom-container title="重点人物">
<!-- 中间内容自定义 -->
<template #default>
<div style="display: flex; gap: 20px">
<div class="renwu-card">
<el-image class="content-img-2" :src="Renwu1" fit="fill" />
<div class="content-desc">
<div class="content-desc-title">吉娜·雷蒙多</div>
<div class="content-desc-content content-desc-content-long">英文名:Gina Raimondo</div>
<div class="content-desc-content content-desc-content-long">党派:美国民主党</div>
</div>
</div>
<div class="renwu-card">
<el-image class="content-img-2" :src="Renwu2" fit="fill" />
<div class="content-desc">
<div class="content-desc-title">艾伦·埃斯特韦斯</div>
<div class="content-desc-content content-desc-content-long">英文名:Alan Estevez</div>
<div class="content-desc-content content-desc-content-long">党派:美国民主党</div>
</div>
</div>
</div>
</template>
</custom-container>
<custom-container title="制裁原因">
<!-- 中间内容自定义 -->
<template #default>
<div style="line-height: 25px; color: rgba(95, 101, 108, 1)">
<div>1.获取美国产物项,以支持中国量子技术;</div>
<div>2.支持2023年2月飞越美国上空的高空气球;</div>
<div>3.获取获取美国原产的无人机物项供中国军方使用。</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="11">
<custom-container title="本次制裁共新增83个实体,其中53个中国大陆实体、1个中国台湾实体。">
<!-- 顶部右侧自定义内容 -->
<!-- <template #header-right>
<div style="display: flex; gap: 12px">
<el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary" icon="Plus">新建项目</el-button>
</div>
</template> -->
<!-- 中间内容自定义 -->
<template #default>
<div class="content-vertical-card">
<el-button-group>
<el-button :icon="Document"> 整体情况</el-button>
<el-button type="primary" :icon="Postcard">
国内分布
<!-- <el-icon class="el-icon--right"><ArrowRight /></el-icon> -->
</el-button>
</el-button-group>
<el-image class="content-distribution-img" :src="Distribution" fit="fill" />
<div style="flex: 1; justify-content: flex-start; width: 100%">
<el-button-group>
<el-button>按类别</el-button>
<el-button type="primary">
按领域
<!-- <el-icon class="el-icon--right"><ArrowRight /></el-icon> -->
</el-button>
</el-button-group>
</div>
</div>
</template>
<!-- 底部自定义内容 -->
<!-- <template #footer>
<el-button>取消</el-button>
<el-button type="primary">保存更改</el-button>
</template> -->
</custom-container>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<custom-container title="美国前序相关制裁、前序相关事件列表">
<template #default>
<!-- <div class="example-card">
<h3>项目介绍</h3>
<p>这是一个示例项目,展示了如何使用自定义容器组件。该组件具有高度可定制性,可以适应各种业务场景的需求。</p>
</div>
<div class="example-card">
<h3>项目进度</h3>
<p>当前项目已完成75%,预计在下个月初完成所有开发任务并进入测试阶段。</p>
<el-progress :percentage="75" :stroke-width="16" />
</div> -->
<div class="time-line">
<div class="time-line-title">
<img class="time-line-dot" src="@/assets/images/dot.png" alt="" />
<span class="time-line-title-text">2025-9-25</span>
</div>
<div class="time-line-desc">
<div class="time-line-desc-title">传感器和仪器仪表技术咨询委员会将于2024年5月9日召开部分闭门会议</div>
<div class="time-line-desc-text">
传感器与仪器仪表技术咨询委员会(以下简称“委员会”)将于美国东部夏令时间2024年5月9日星期四下午1:00至2:30召开会议。本次会议将通过微软团队(MS
Teams)以线上形式举行。根据美国商务部第10-16号《部门组织》文件。
</div>
</div>
</div>
<div class="time-line">
<div class="time-line-title">
<img class="time-line-dot" src="@/assets/images/dot.png" alt="" />
<span class="time-line-title-text">2025-4-25</span>
</div>
<div class="time-line-desc">
<div class="time-line-desc-title">中美关税战升级至125%与反制措施</div>
<div class="time-line-desc-text">
美国将对华关税从34%提升至84%(总税率104%),中方同步对美商品加征同等税率,并暂停进口美国影片、限制留学合作。特朗普政府通过"基准关税+对等关税+额外加征"策略施压,引发全球供应链震荡。
</div>
</div>
</div>
<div class="time-line">
<div class="time-line-title">
<img class="time-line-dot" src="@/assets/images/dot.png" alt="" />
<span class="time-line-title-text">2025-4-17</span>
</div>
<div class="time-line-desc">
<div class="time-line-desc-title">美国对华加征关税至20%并扩大科技制裁议</div>
<div class="time-line-desc-text">
美国以"芬太尼问题"为由提高对华关税至20%,恢复钢铁铝关税;同时推动《恢复贸易公平法案》,计划对中国商品征收100%关税。系统性打压中国高科技产业,关税措施实质为经济勒索。
</div>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</div>
</template>
<script setup>
import CustomContainer from "@/components/Container/index.vue";
import BISlogo from "@/assets/images/BISlogo.png";
import Distribution from "@/assets/images/distribution.png";
import Renwu1 from "@/assets/images/renwu-1.png";
import Renwu2 from "@/assets/images/renwu-2.png";
import { Document } from "@element-plus/icons-vue";
import { Postcard } from "@element-plus/icons-vue";
</script>
<style scoped>
.content-card {
display: flex;
align-items: flex-start;
gap: 20px;
}
.content-vertical-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.renwu-card {
display: flex;
align-items: center;
gap: 20px;
width: 379px;
height: 148px;
background-color: rgba(234, 236, 238, 1);
padding-left: 10px;
}
.content-img {
width: 154px;
height: 154px;
flex-shrink: 0;
}
.content-img-2 {
width: 128px;
height: 128px;
flex-shrink: 0;
}
.content-distribution-img {
width: 627px;
height: 536px;
}
.content-desc {
display: flex;
flex-direction: column;
gap: 8px;
}
.content-desc-item {
display: flex;
gap: 4px;
}
.content-desc-title {
width: 100px;
flex-shrink: 0;
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
}
.content-desc-content {
color: var(--color-main-active);
font-size: 16px;
font-weight: 700;
}
.content-desc-content-long {
color: rgba(95, 101, 108, 1);
font-weight: 400;
}
.time-line {
display: flex;
gap: 20px;
align-items: flex-start;
padding-left: 10px;
margin-bottom: 20px;
}
.time-line-title {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.time-line-dot {
width: 10px;
height: 10px;
}
.time-line-title-text {
font-size: 14px;
font-weight: 700;
color: rgba(95, 101, 108, 1);
color: var(--base-color);
}
.time-line-desc-title {
font-size: 16px;
font-weight: 400;
color: var(--base-color);
margin-bottom: 5px;
}
.time-line-desc-text {
font-size: 14px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
}
.time-line-dot {
width: 8px;
height: 8px;
background-color: var(--color-main-active);
border-radius: 50%;
}
.example-card {
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 16px;
margin-bottom: 16px;
background-color: #fafafa;
}
.example-card h3 {
color: #409eff;
margin-bottom: 8px;
}
.example-card p {
color: #606266;
line-height: 1.6;
}
</style>
<template>
<div class="sanction-container">
<!-- 页面标题区域 -->
<div class="page-header">
<!-- <h1 class="main-title">制裁分析</h1> -->
<div class="bill-info">
<div class="bill-details">
<div class="main-title">制裁分析</div>
<div class="bill-name-en">第119届美国国会众议院第1号法案One Big Beautiful Bill Act</div>
</div>
<div class="date-author">
<div class="date">2025年7月</div>
<div class="author">乔迪·阿灵顿(Jodey Arrington)</div>
</div>
</div>
</div>
<!-- 导航标签区域 -->
<!-- <div class="nav-section">
<div class="tabs">
<div class="tab-item active">制裁概况</div>
<div class="tab-item">深度挖掘</div>
<div class="tab-item">影响分析</div>
</div>
<div class="action-buttons">
<el-button>法案原文</el-button>
<el-button type="primary">分析报告</el-button>
</div>
</div> -->
<!-- 内容区域 -->
<!-- <div class="content-section">
<div class="content-placeholder">
<el-icon><document /></el-icon>
<p>这里是制裁概况的内容区域</p>
<p>请选择上方标签查看不同分析内容</p>
</div>
</div> -->
</div>
</template>
<script setup></script>
<style scoped>
.sanction-container {
width: 100%;
/* max-width: 1200px; */
background-color: #fff;
/* border-radius: 8px; */
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
box-sizing: border-box;
padding-left: 5%;
padding-right: 5%;
}
.page-header {
padding: 24px;
padding-top: 0;
/* border-bottom: 1px solid #ebeef5; */
}
.main-title {
font-size: 20px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
/* color: var(--base-color); */
margin-bottom: 8px;
}
.bill-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
}
.bill-details {
flex: 1;
}
.bill-name {
font-size: 16px;
color: #606266;
margin-bottom: 4px;
}
.bill-name-en {
font-size: 14px;
color: #909399;
font-style: italic;
}
.date-author {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.date {
font-size: 14px;
color: #606266;
margin-bottom: 4px;
}
.author {
font-size: 14px;
color: #606266;
}
.nav-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background-color: #f8fafc;
border-bottom: 1px solid #ebeef5;
}
.tabs {
display: flex;
gap: 0;
}
.tab-item {
padding: 12px 24px;
font-size: 14px;
color: #606266;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #409eff;
border-bottom-color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.tab-item:hover {
color: #409eff;
}
.action-buttons {
display: flex;
gap: 12px;
}
.content-section {
padding: 24px;
min-height: 400px;
}
.content-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
color: #909399;
}
.content-placeholder .el-icon {
font-size: 48px;
margin-bottom: 16px;
color: #dcdfe6;
}
.content-placeholder p {
font-size: 16px;
margin-top: 8px;
}
@media (max-width: 768px) {
.bill-info {
flex-direction: column;
align-items: flex-start;
}
.date-author {
align-items: flex-start;
margin-top: 12px;
}
.nav-section {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.tabs {
width: 100%;
overflow-x: auto;
}
}
</style>
<template>
<div class="page-container">
<Header />
<!-- 导航标签区域 -->
<div class="nav-section">
<div class="tabs">
<div :class="['tab-item', { active: activeTab === '制裁概况' }]" @click="setActiveTab('制裁概况')">制裁概况</div>
<div :class="['tab-item', { active: activeTab === '深度挖掘' }]" @click="setActiveTab('深度挖掘')">深度挖掘</div>
<div :class="['tab-item', { active: activeTab === '影响分析' }]" @click="setActiveTab('影响分析')">影响分析</div>
</div>
<div class="action-buttons">
<el-button>法案原文</el-button>
<el-button type="primary">分析报告</el-button>
</div>
</div>
<!-- 内容区域 -->
<div class="content-section">
<!-- <div class="content-placeholder">
<el-icon><document /></el-icon>
<p>这里是制裁概况的内容区域</p>
<p>请选择上方标签查看不同分析内容</p>
</div> -->
<Survey v-if="activeTab === '制裁概况'" />
<DepthMine v-if="activeTab === '深度挖掘'" />
<ImpactAnalysis v-if="activeTab === '影响分析'" />
</div>
<div class="left-absolute">
<el-image :src="Left1" alt="" />
<el-image :src="Left2" alt="" />
<el-image :src="Left3" alt="" />
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import Header from "./header.vue";
import Survey from "./content/survey.vue";
import DepthMine from "./content/depthMine.vue";
import ImpactAnalysis from "./content/impactAnalysis.vue";
import Left1 from "@/assets/images/left-1.png";
import Left2 from "@/assets/images/left-2.png";
import Left3 from "@/assets/images/left-3.png";
const activeTab = ref("制裁概况");
const setActiveTab = tabName => {
activeTab.value = tabName;
};
</script>
<style scoped lang="scss">
.page-container {
position: relative;
margin: 0px auto;
background-color: rgba(247, 248, 249, 1);
}
.nav-section {
display: flex;
justify-content: space-between;
align-items: center;
/* padding: 16px 24px; */
padding-left: 6%;
padding-right: 6%;
background-color: #fff;
border-bottom: 1px solid #ebeef5;
}
.tabs {
display: flex;
gap: 0;
}
.tab-item {
width: 120px;
height: 40px;
text-align: center;
line-height: 30px;
// padding: 12px 24px;
font-size: 18px;
color: #606266;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.3s;
box-sizing: border-box;
}
// .tab-item.active {
// color: #409eff;
// border-bottom-color: #409eff;
// background-color: rgba(64, 158, 255, 0.1);
// }
.tab-item.active {
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 30px;
border-bottom: 3px solid var(--color-main-active);
}
.tab-item:hover {
color: var(--color-main-active);
}
.action-buttons {
display: flex;
gap: 12px;
}
.content-section {
padding: 24px;
min-height: 400px;
}
.content-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
color: #909399;
}
.content-placeholder .el-icon {
font-size: 48px;
margin-bottom: 16px;
color: #dcdfe6;
}
.content-placeholder p {
font-size: 16px;
margin-top: 8px;
}
@media (max-width: 768px) {
.bill-info {
flex-direction: column;
align-items: flex-start;
}
.date-author {
align-items: flex-start;
margin-top: 12px;
}
.nav-section {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.tabs {
width: 100%;
overflow-x: auto;
}
}
.left-absolute {
position: fixed;
left: 0;
top: 60%;
// width: 48px;
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
background-color: #fff;
border-radius: 10px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
padding: 16px 12px;
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
div {
width: 17px;
height: 18px;
}
}
</style>
<template>
<div class="message-bubble">
<div class="avatar-container">
<img :src="avatar" :alt="name" class="avatar" />
</div>
<div class="bubble-container">
<div class="bubble">
<div class="bubble-header">
<span class="name">{{ name }}</span>
<span class="meta">{{ time }} · {{ source }}</span>
</div>
<div class="bubble-content">
{{ content }}
</div>
<div class="triangle"></div>
</div>
</div>
</div>
</template>
<script setup>
defineProps({
avatar: {
type: String,
default: "https://via.placeholder.com/40x40/4A90E2/FFFFFF?text=T"
},
name: {
type: String,
default: "唐纳德·特朗普"
},
time: {
type: String,
default: "15:23"
},
source: {
type: String,
default: "发布于真实社交"
},
content: {
type: String,
default:
"埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。"
}
});
</script>
<style scoped>
.message-bubble {
display: flex;
max-width: 700px;
margin: 20px 0;
}
.avatar-container {
flex-shrink: 0;
margin-right: 12px;
}
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
}
.bubble-container {
flex: 1;
position: relative;
}
.bubble {
background-color: rgba(246, 250, 255, 1);
border-radius: 12px;
padding: 12px 16px;
position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.bubble-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.name {
font-weight: bold;
font-size: 16px;
color: #333;
}
.meta {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: right;
}
.bubble-content {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.triangle {
position: absolute;
left: -8px;
top: 15px;
width: 0;
height: 0;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid rgba(246, 250, 255, 1);
}
/* 响应式设计 */
@media (max-width: 768px) {
.message-bubble {
max-width: 100%;
}
.bubble-header {
flex-direction: column;
align-items: flex-start;
}
.meta {
margin-top: 4px;
}
}
</style>
<template>
<div class="info-card">
<div class="color-bar" :style="{ backgroundColor: color }"></div>
<div class="card-content">
<div class="title-section">
<div class="main-title">{{ title }}</div>
<div class="sub-title">{{ subtitle }}</div>
</div>
<div class="description">{{ description }}</div>
<div v-if="quantity > 0" class="quantity" :style="{ color: color }">{{ quantity }}</div>
</div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: "标题"
},
subtitle: {
type: String,
default: "Subtitle"
},
description: {
type: String,
default: "描述信息"
},
quantity: {
type: [Number, String],
default: 0
},
color: {
type: String,
default: "#409EFF"
}
});
</script>
<style scoped>
.info-card {
max-width: 388px;
min-width: 300px;
height: 150px;
border-radius: 12px;
background-color: white;
display: flex;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.info-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.color-bar {
width: 6px;
height: 120px;
flex-shrink: 0;
margin-top: 15px;
}
.card-content {
flex: 1;
padding: 20px;
display: flex;
flex-direction: column;
position: relative;
}
.title-section {
margin-bottom: 12px;
}
.main-title {
font-size: 24px;
font-weight: 700;
color: #1f2937;
line-height: 1.2;
}
.sub-title {
font-size: 16px;
font-weight: 700;
line-height: 1.2;
color: #6b7280;
margin-top: 2px;
}
.description {
font-size: 16px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
line-height: 1.4;
flex: 1;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.quantity {
position: absolute;
top: 20px;
right: 20px;
font-size: 32px;
font-weight: 700;
}
</style>
<template>
<div :class="['clickable-card', { disabled: disabled }]" @click="handleClick">
<span class="card-text">{{ text }}</span>
<!-- <span class="arrow-icon"></span> -->
<div class="icon">
<el-icon><ArrowRightBold /></el-icon>
</div>
</div>
</template>
<script setup>
import { ArrowRight, ArrowRightBold } from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
const router = useRouter();
const props = defineProps({
text: {
type: String,
default: "点击跳转"
},
link: {
type: String,
default: ""
},
target: {
type: String,
default: "_self"
},
disabled: {
type: Boolean,
default: false
}
});
const emit = defineEmits(["click"]);
const handleClick = () => {
if (props.disabled) return;
emit("click");
if (props.link) {
if (props.link.startsWith("http") || props.link.startsWith("//")) {
// 外部链接
window.open(props.link, props.target);
} else {
router.push(props.link);
console.log(`跳转到: ${props.link}`);
}
}
};
</script>
<style lang="scss" scoped>
.clickable-card {
width: 160px;
height: 48px;
border-radius: 32px;
border: 1px solid rgba(174, 214, 255, 1);
background-color: rgba(231, 243, 255, 1);
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
/* padding: 0 16px; */
cursor: pointer;
transition: all 0.3s ease;
color: rgba(5, 95, 194, 1);
font-size: 20px;
font-weight: 500;
box-sizing: border-box;
position: relative;
.icon{
position: absolute;
top: 14px;
right: 12px;
}
}
.clickable-card:hover {
background-color: rgba(231, 243, 255, 0.5);
box-shadow: 0 2px 8px rgba(5, 95, 194, 0.1);
transform: translateY(-2px);
}
.clickable-card:active {
transform: translateY(0);
box-shadow: 0 1px 4px rgba(5, 95, 194, 0.1);
}
.clickable-card.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.clickable-card.disabled:hover {
background-color: white;
box-shadow: none;
transform: none;
}
.arrow-icon {
font-weight: bold;
font-size: 16px;
}
</style>
<template>
<div class="news-list">
<div v-for="(item, index) in listData" :key="index" class="news-item" @click="handleItemClick(item)">
<div class="news-image">
<img :src="item.image" :alt="item.title" />
</div>
<div class="news-content">
<div class="news-header">
<div class="news-title">{{ item.title }}</div>
<div class="news-meta">
<span class="news-time">{{ item.time }}</span> ·
<span class="news-source">{{ item.source }}</span>
</div>
</div>
<div class="news-description">{{ item.description }}</div>
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
listData: {
type: Array,
default: () => [
{
image: "https://via.placeholder.com/72x48/4A90E2/FFFFFF?text=News",
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析指出..."
}
]
}
});
const emit = defineEmits(["item-click"]);
const handleItemClick = item => {
emit("item-click", item);
};
</script>
<style scoped>
.news-list {
width: 100%;
max-width: 800px;
}
.news-item {
display: flex;
padding: 10px 0;
align-items: center;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.3s;
}
.news-item:hover {
background-color: #f9f9f9;
}
.news-item:last-child {
border-bottom: none;
}
.news-image {
width: 72px;
height: 48px;
flex-shrink: 0;
margin-right: 16px;
border-radius: 4px;
overflow: hidden;
}
.news-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.news-content {
flex: 1;
display: flex;
flex-direction: column;
}
.news-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
}
.news-title {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
line-height: 24px;
flex: 1;
margin-right: 12px;
}
.news-meta {
display: flex;
align-items: center;
height: 24px;
line-height: 24px;
flex-shrink: 0;
font-size: 14px;
color: #999;
}
.news-time {
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
.news-source {
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
.news-description {
font-size: 16px;
color: rgba(59, 65, 75, 1);
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<template>
<div class="custom-title">
<div class="color-block block1"></div>
<div class="color-block block2"></div>
<div class="title-text">{{ title }}</div>
<div class="color-line"></div>
</div>
</template>
<script setup>
defineProps({
title: {
type: String,
default: "最新动态"
}
});
</script>
<style scoped>
.custom-title {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 20px;
padding: 10px 15px;
}
.color-block {
background-color: rgba(174, 214, 255, 1);
height: 30px;
flex-shrink: 0;
}
.block1 {
width: 24px;
}
.block2 {
width: 8px;
margin-left: 10px;
}
.title-text {
font-size: 32px;
font-weight: 700;
margin-left: 20px;
white-space: nowrap;
}
.color-line {
background-color: rgba(174, 214, 255, 1);
height: 2px;
flex-grow: 1;
margin-left: 20px;
}
</style>
<template>
<div class="home-wrapper">
<!-- <div class="home-header">
<div class="header-left">
<HeaderMenu></HeaderMenu>
</div>
<div class="header-right">
<headerInfo curTitleName="投融资限制"></headerInfo>
</div>
</div> -->
<div class="home-main" ref="containerRef">
<div class="home-top-bg"></div>
<div class="home-main-header">
<div class="home-main-header-center">
<SearchContainer
style="margin-bottom: 0; height: fit-content"
v-if="containerRef"
placeholder="搜索投融资限制政策"
:containerRef="containerRef"
areaName=""
/>
<!-- <el-input
v-model="searchKey"
style="width: 100%; height: 48px"
size="large"
placeholder="搜索出口管制调查"
:suffix-icon="Search"
/> -->
<!-- <el-input v-model="searchKey" style="width: 800px; height: 100%" placeholder="搜索出口管制政策" />
<div class="search">
<div class="search-icon">
<img src="@/assets/icons/search-icon.png" alt="" />
</div>
<div class="search-text">搜索</div>
</div> -->
</div>
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">142</div>
<div class="item-footer">实体清单</div>
</div>
<div class="home-main-header-footer-item">
<div class="item-top">28</div>
<div class="item-footer">商业管制清单</div>
</div>
<div class="home-main-header-footer-item">
<div class="item-top">326</div>
<div class="item-footer">关键和新型技术清单</div>
</div>
</div> -->
<!-- <div class="home-main-header-btn-box">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position2')">
<div class="btn-text">{{ "资讯要闻" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position3')">
<div class="btn-text">{{ "统计概览" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
<div class="btn" @click="scrollToTop('position4')">
<div class="btn-text">{{ "资源库" }}</div>
<div class="btn-icon">
<img src="@/assets/icons/arrow-right-icon.png" alt="" />
</div>
</div>
</div> -->
<!-- <div class="home-main-header-footer-link">
<ClickableCard text="最新动态" @click="scrollToTop('position1')" target="_blank" />
<ClickableCard text="资讯要闻" @click="scrollToTop('position2')" target="_blank" />
<ClickableCard text="数据总览" @click="scrollToTop('position3')" target="_blank" />
<ClickableCard text="资源库" @click="scrollToTop('position4')" target="_blank" />
</div> -->
<div class="home-main-header-footer-info">
<!-- <div class="card" v-for="(item, index) in infoList" :key="index">
<div class="icon" :style="{ background: item.color }"></div>
<div class="title">{{ item.title }}</div>
<div class="content">{{ item.des }}</div>
<div class="num" :style="{ color: item.color }">{{ item.num + "项" }}</div>
</div> -->
<InfoCard
v-for="(item, index) in infoList"
:key="item.id"
:title="item.nameZh"
:subtitle="item.nameAbbr"
:description="item.description"
:quantity="item.postCount"
unit="个"
:color="infoListColor[index]"
@click="handleToEntityListNoId(item)"
/>
</div>
</div>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto">
<CustomTitle id="position1" title="最新动态" style="margin-top: 24px" />
<el-col :span="16">
<custom-container titleType="primary" title="最新投融资限制政策" :titleIcon="houseIcon" height="450px">
<template #header-right>
<el-button type="primary" link>
{{ "查看详情 >" }}
</el-button>
</template>
<template #default>
<div class="box1">
<div class="box1-top">
<div class="box1-top-title">2025年9月12日——BIS《实体清单增列与修订条目》</div>
<div class="box1-top-content">
<div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 发布机构:</span>
<span class="box1-top-content-item-content">美国商务部工业与安全局</span>
</div>
<div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 生效日期:</span>
<span class="box1-top-content-item-content">2025年9月12日</span>
</div>
<div class="box1-top-content-item">
<span class="box1-top-content-item-title">· 涉及领域:</span>
<div
class="box1-top-content-item-tags"
v-for="item in ['航空航天', '人工智能', '集成电路']"
:key="item"
>
<el-tag
:type="
item === '航空航天' ? 'primary' : item === '人工智能' ? 'danger' : 'info'
"
>{{ item }}</el-tag
>
</div>
</div>
</div>
</div>
<div class="box1-bottom">
<div class="box1-bottom-title">· 涉及主要实体:</div>
<div class="box1-bottom-content">
<div class="box1-bottom-content-item" v-for="(item, index) in entityList" :key="index">
<el-image class="box1-bottom-content-item-img" :src="item.img" alt=""></el-image>
<div class="box1-bottom-content-item-txt">{{ item.name }}</div>
</div>
</div>
</div>
<div class="box1-absolute">
<div class="box1-absolute-des">
<el-icon>
<Warning color="rgba(206, 79, 81, 1)" />
</el-icon>
<span>新增中国实体</span>
</div>
<div class="box1-absolute-num">23家</div>
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="8">
<!-- <custom-container titleType="danger" title="风险信号" :titleIcon="dangerIcon" height="450px">
<template #default>
<div class="box2-main">
<div class="box2-main-item" v-for="(item, index) in warningList" :key="index">
<div
class="item-left"
:class="{
itemLeftStatus1: item.status === '一般风险',
itemLeftStatus2: item.status === '重大风险'
}"
>
{{ item.status }}
</div>
<div class="item-right">
<div class="text">
{{ item.title }}
</div>
<div class="time">{{ item.time }}</div>
</div>
</div>
<div class="box2-footer" @click="handleToMoreRiskSignal">
<div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" />
</div>
<div class="text">{{ "查看更多" }}</div>
</div>
</div>
</template>
</custom-container> -->
<RiskSignal
:list="warningList"
@item-click="handleToRiskSignalDetail"
@more-click="handleToMoreRiskSignal"
riskLevel="status"
postDate="time"
name="title"
/>
</el-col>
</el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto">
<CustomTitle id="position2" title="资讯要闻" style="margin-top: 64px" />
</el-row>
<!-- <el-col :span="12">
<custom-container title="新闻资讯" :titleIcon="newsIcon" height="450px">
<template #header-right>
<el-button type="primary" link @click="handleToMoreNews">
{{ "更多 +" }}
</el-button>
</template>
<template #default>
<div class="news-list">
<NewsList :list-data="customNewsData" />
</div>
</template>
</custom-container>
</el-col> -->
<div class="center-center">
<div class="center-center-news">
<NewsList
:newsList="customNewsData"
@item-click="handleNewsInfoClick"
@more-click="handleToMoreNews"
from="from"
content="description"
title="title"
img="image"
/>
</div>
<MessageBubble
:messageList="messageList"
@person-click="handlePerClick"
imageUrl="avatar"
@more-click="handleToSocialDetail"
/>
<!-- <div class="boxs4">
<custom-container title="社交媒体" :titleIcon="dialogIcon" height="450px">
<template #default>
<div class="dialog-list">
<!-- <MessageBubble
:avatar="customMessage.avatar"
:name="customMessage.name"
:time="customMessage.time"
:source="customMessage.source"
:content="customMessage.content"
/> --
<MessageBubble :avatar="trumpAvatar" name="唐纳德·特朗普" time="16:02" source="发布于真实社交"
content="埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。" />
<MessageBubble :avatar="elongAvatar" name="埃隆·马斯克" time="16:02" source="发布于真实社交"
content="如果这个疯狂的支出法案获得通过,‘美国党’将在第二天成立。" />
<MessageBubble :avatar="elongAvatar" name="埃隆·马斯克" time="16:02" source="发布于真实社交"
content="提出特朗普政府的AI政策强调技术开放与快速应用,但可能以牺牲安全防范为代价,开启了“潘多拉魔盒”。" />
</div>
</template>
</custom-container>
</div> -->
</div>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto">
<CustomTitle id="position3" title="数据总览" style="margin-top: 64px" />
<el-col :span="24">
<custom-container title="发布频度" :titleIcon="box3Icon" height="450px">
<template #default>
<div class="box3">
<div class="box3-content">
<div class="box3-content-title">实体清单发布频度</div>
<el-table :data="tableData1" stripe style="width: 100%">
<el-table-column prop="year" label="年份" width="100" />
<el-table-column label="发布次数" width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress
:percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div>
</template>
</el-table-column>
<el-table-column label="重点领域" width="180">
<template #default="scope">
<div style="display: flex; align-items: center; gap: 5px">
<el-tag
v-for="tag in scope.row.tags"
:key="tag"
:type="
tag === '通信网络'
? 'primary'
: TAGTYPE[Math.floor(Math.random() * 5)]
"
>{{ tag }}</el-tag
>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="box3-content">
<div class="box3-content-title">商业管制清单(CCL)更新频度</div>
<el-table :data="tableData1" stripe style="width: 100%">
<el-table-column prop="year" label="年份" width="100" />
<el-table-column label="发布次数" width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress
:percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div>
</template>
</el-table-column>
<el-table-column label="重点领域" width="180">
<template #default="scope">
<div style="display: flex; align-items: center; gap: 5px">
<el-tag
v-for="tag in scope.row.tags"
:key="tag"
:type="
tag === '通信网络'
? 'primary'
: TAGTYPE[Math.floor(Math.random() * 5)]
"
>{{ tag }}</el-tag
>
</div>
</template>
</el-table-column>
</el-table>
</div>
<div class="box3-content">
<div class="box3-content-title">关键与新兴技术清单(CETs)</div>
<el-table :data="tableData1" stripe style="width: 100%">
<el-table-column prop="year" label="年份" width="100" />
<el-table-column label="发布次数" width="180">
<template #default="scope">
<div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 40px">{{ scope.row.num }}</span>
<el-progress
:percentage="scope.row.percent * 100"
:show-text="false"
:status="getStatus(scope.row.percent)"
/>
</div>
</template>
</el-table-column>
<el-table-column label="重点领域" width="180">
<template #default="scope">
<div style="display: flex; align-items: center; gap: 5px">
<el-tag
v-for="tag in scope.row.tags"
:key="tag"
:type="
tag === '通信网络'
? 'primary'
: TAGTYPE[Math.floor(Math.random() * 5)]
"
>{{ tag }}</el-tag
>
</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row gutter="20" style="width: 1600px; margin: 0 auto">
<el-col :span="8">
<custom-container title="制裁领域分析" :titleIcon="radarIcon" height="450px">
<template #header-right>
<el-checkbox v-model="checked" label="50%规则" size="large" />
</template>
<template #default>
<EChart :option="radarOption" autoresize :style="{ height: '400px' }" />
</template>
</custom-container>
</el-col>
<el-col :span="16">
<custom-container title="制裁清单数量增长趋势" :titleIcon="qushiIcon" height="450px">
<template #header-right>
<el-checkbox v-model="checked" label="50%规则" size="large" />
</template>
<template #default>
<EChart :option="trendOption" autoresize :style="{ height: '400px' }" />
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20" style="width: 1600px; margin: 0 auto">
<CustomTitle id="position4" title="资源库" style="margin-top: 54px" />
<el-col :span="8">
<custom-container title="制裁历程" :titleIcon="listIcon" height="845px">
<template #default>
<div class="box4">
<div class="box4-item" v-for="(item, idx) in sanctionProcessList" :key="item.title">
<div class="box4-item-left">
<el-image :src="dotIcon" alt="图片" class="box4-item-left-icon" />
<div class="box4-item-left-line" v-if="idx + 1 != sanctionProcessList.length"></div>
</div>
<div class="box4-item-right">
<div class="box4-item-right-header">
<span class="box4-item-right-header-title">{{ item.title }}</span>
<span class="box4-item-right-header-desc">{{ item.desc }}</span>
</div>
<div class="box4-item-right-content">
{{ item.content }}
</div>
</div>
</div>
<div class="box4-footer">
<el-button type="primary" link :icon="DownRight"
>查看更多
<el-icon>
<DArrowRight />
</el-icon>
</el-button>
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="16">
<custom-container title="制裁实体清单" :titleIcon="entityIcon" height="845px">
<template #header-right>
<div class="box5-header-right">共1329家实体</div>
</template>
<template #default>
<div class="box5">
<el-table
:data="paginatedData"
class="sanction-table"
stripe
empty-text="暂无数据"
height="700px"
header-row-class-name="table-header"
row-class-name="table-row"
>
<!-- <el-table-column prop="index" label="序号" width="80" align="center">
<template #default="scope">
{{ scope.$index + 1 + (currentPage - 1) * pageSize }}
</template>
</el-table-column> -->
<el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope">
<div
style="
font-weight: 700;
font-size: 16px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
"
>
{{ scope.row.name }}
</div>
</template>
</el-table-column>
<el-table-column prop="domains" label="涉及领域" min-width="180">
<template #default="scope">
<div class="domain-tags">
<el-tag
v-for="tag in scope.row.domains"
:key="tag"
:type="tag === '通信网络' ? 'primary' : 'danger'"
>{{ tag }}</el-tag
>
</div>
</template>
</el-table-column>
<el-table-column prop="sanctionDate" label="上市地点" width="120" align="center">
<template #default="scope">
{{ scope.row.listingLocation ? scope.row.listingLocation : "--" }}
</template>
</el-table-column>
<el-table-column prop="sanctionDate" label="制裁时间" width="120" align="center">
<template #default="scope">
{{ scope.row.sanctionDate }}
</template>
</el-table-column>
<el-table-column prop="strength" label="制裁强度" width="120" align="center">
<template #default="scope">
<div class="sanction-strength">
<div :class="['strength-bar', `strength-${scope.row.strength}`]"></div>
<span>{{ strengthLabels[scope.row.strength] }}</span>
</div>
</template>
</el-table-column>
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="140" align="right">
<template #default="scope">
<span
:class="['revenue-cell', scope.row.revenue === '无营收数据' ? 'no-revenue' : '']"
>
{{ scope.row.revenue }}
</span>
</template>
</el-table-column> -->
<el-table-column prop="revenue" label="50%规则子企业" width="140" align="right">
<template #default="scope">
<span
:class="['revenue-cell', scope.row.revenue === '无营收数据' ? 'no-revenue' : '']"
>
{{ scope.row.revenue }}
</span>
</template>
</el-table-column>
</el-table>
<div class="table-footer">
<!-- <div class="pagination-info">
第{{ currentPage }}页,共{{ totalPages }}页
</div> -->
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="total"
:pager-count="5"
layout="prev, pager, next"
background
/>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import RiskSignal from "@/components/base/riskSignal/index.vue";
import { onMounted, ref, computed } from "vue";
import scrollToTop from "@/utils/scrollToTop";
import setChart from "@/utils/setChart";
import * as echarts from "echarts";
import { DArrowRight, Warning, Search } from "@element-plus/icons-vue";
import EChart from "@/components/Chart/index.vue";
import { TAGTYPE } from "@/public/constant";
import { useRouter } from "vue-router";
const router = useRouter();
import CustomContainer from "@/components/Container/index.vue";
import ClickableCard from "./components/link.vue";
import InfoCard from "./components/info.vue";
import CustomTitle from "./components/title.vue";
import NewsList from "@/components/base/newsList/index.vue";
import trumpAvatar from "@/assets/images/icon-trump.png";
import elongAvatar from "@/assets/images/icon-elong.png";
import newsIcon from "@/assets/images/icon-news.png";
import dialogIcon from "@/assets/images/icon-duihua.png";
import houseIcon from "@/assets/images/icon-house.png";
import dangerIcon from "./assets/images/box2-header-icon.png";
import box1Image from "./assets/images/box1-image.png";
import box3Icon from "./assets/images/box1-header-icon.png";
import radarIcon from "./assets/images/icon-radar.png";
import qushiIcon from "./assets/images/icon-qushi.png";
import listIcon from "./assets/images/icon-list.png";
import dotIcon from "./assets/images/info2-icon.png";
import entityIcon from "./assets/images/icon-entity.png";
import newsImg from "@/assets/images/news-img.png";
import getMultiLineChart from "./utils/multiLineChart";
import bill1 from "./assets/images/bill1.png";
import bill2 from "./assets/images/bill2.png";
import bill3 from "./assets/images/bill3.png";
import bill4 from "./assets/images/bill4.png";
import bill5 from "./assets/images/bill5.png";
import bill6 from "./assets/images/bill6.png";
import bill7 from "./assets/images/bill7.png";
import bill8 from "./assets/images/bill8.png";
import bill9 from "./assets/images/bill9.png";
import bill10 from "./assets/images/bill10.png";
import bill11 from "./assets/images/bill11.png";
import bill12 from "./assets/images/bill12.png";
import fudanIcon from "./assets/images/fudan.png";
import fukongIcon from "./assets/images/fukong.png";
import hualingIcon from "./assets/images/hualing.png";
import jimuxiIcon from "./assets/images/jimuxi.png";
import shenggongIcon from "./assets/images/shenggong.png";
import shoushiIcon from "./assets/images/shoushi.png";
import tianyiIcon from "./assets/images/tianyi.png";
import aircasIcon from "./assets/images/aircas.png";
// 父组件中定义 messageList 数据(avatar 对应子组件 imageUrl 字段,需用 import 的图片才能正确显示)
const messageList = ref([
{
avatar: trumpAvatar,
name: "唐纳德·特朗普",
time: "2025-05-12 16:02:37",
source: "真实社交",
content:
"埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。"
},
{
avatar: elongAvatar,
name: "埃隆·马斯克",
time: "2025-05-12 16:02:37",
source: "真实社交",
content: "如果这个疯狂的支出法案获得通过,‘美国党’将在第二天成立。"
},
{
avatar: elongAvatar,
name: "埃隆·马斯克",
time: "2025-05-12 16:02:37",
source: "真实社交",
content: "提出特朗普政府的AI政策强调技术开放与快速应用,但可能以牺牲安全防范为代价,开启了“潘多拉魔盒”。"
}
]);
// 查看更多风险信号
const handleToMoreRiskSignal = () => {
const route = router.resolve("/viewRiskSignal");
window.open(route.href, "_blank");
};
const infoListColor = ref(["rgba(206, 79, 81, 1)", "rgba(114, 46, 209, 1)", "rgba(132, 136, 142, 1)", "rgba(132, 136, 142, 1)"]);
// 查看更多新闻资讯
const handleToMoreNews = () => {
const route = router.resolve("/newsBrief");
window.open(route.href, "_blank");
};
let containerRef = ref(null);
const billList = ref([]);
const curBillListIndex = ref(0);
const searchKey = ref("");
const infoList = ref([
{
title: "SDN清单",
des: "由美国财政部海外资产控制办公室(OFAC)管理,通过切断目标实体或个人与美国金融体系的联系,以实现美国的外交政策和国家安全目标。",
num: 87,
color: "rgba(5, 95, 194, 1)"
},
{
title: "CMIC清单",
des: "由美国财政部海外资产控制办公室(OFAC)管理,通过切断目标实体或个人与美国金融体系的联系,以实现美国的外交政策和国家安全目标。",
num: 3,
color: "rgba(114, 46, 209, 1)"
}
]);
const entityList = ref([
{
name: "北京复旦微电子技术有限公司",
img: fudanIcon
},
{
name: "北京福康微电子技术有限公司",
img: fukongIcon
},
{
name: "北京华岭微电子技术有限公司",
img: hualingIcon
},
{
name: "北京吉姆微芯科技有限公司",
img: jimuxiIcon
},
{
name: "北京盛工微电子技术有限公司",
img: shenggongIcon
},
{
name: "北京首时微电子技术有限公司",
img: shoushiIcon
},
{
name: "北京天易微电子技术有限公司",
img: tianyiIcon
},
{
name: "北京航空航天大学微电子技术研究院",
img: aircasIcon
}
]);
const customNewsData = ref([
{
newsImage: newsImg,
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
from: "11-4 · 华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析..."
},
{
newsImage: newsImg,
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
from: "11-4 · 华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析..."
},
{
newsImage: newsImg,
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
from: "11-4 · 华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析..."
},
{
newsImage: newsImg,
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
from: "11-4 · 华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析..."
},
{
newsImage: newsImg,
title: "美国智库激辩人工智能监管路径",
time: "11-4",
source: "华盛顿邮报",
from: "11-4 · 华盛顿邮报",
description: "各方就AI监管模式展开讨论。有观点认为碎片化州级监管比全面联邦法规更灵活,也有分析..."
}
]);
// 发布频度
const tableData1 = ref([
{
year: "2025",
num: 5,
percent: 0.4,
tags: ["通信网络"]
},
{
year: "2024",
num: 12,
percent: 0.9,
tags: ["通信网络", "人工智能"]
},
{
year: "2023",
num: 2,
percent: 0.4,
tags: ["通信网络"]
},
{
year: "2022",
num: 6,
percent: 0.7,
tags: ["通信网络", "集成电路"]
},
{
year: "2021",
num: 7,
percent: 0.8,
tags: ["生物科技", "集成电路"]
}
]);
// 雷达图
const radarOption = ref({
title: {
text: ""
},
// grid: {
// top: 100
// },
legend: {
top: 0,
icon: "circle",
data: ["实体清单", "商业管制清单", "关键和新型技术清单"]
},
radar: {
// shape: 'circle',
indicator: [
{ name: "集成电路", max: 6500 },
{ name: "生物科技", max: 16000 },
{ name: "人工智能", max: 30000 },
{ name: "通信网络", max: 38000 },
{ name: "量子科技", max: 52000 },
{ name: "能源领域", max: 25000 }
],
axisName: {
formatter: "{value}",
color: "rgba(59, 65, 75, 1)",
fontSize: 16,
fontWeight: 700
}
},
series: [
{
name: "Budget vs spending",
type: "radar",
data: [
{
value: [4200, 3000, 20000, 35000, 50000, 18000],
name: "实体清单",
areaStyle: {
color: "rgba(10, 87, 166, 0.2)"
}
},
{
value: [5000, 14000, 28000, 26000, 42000, 21000],
name: "商业管制清单",
areaStyle: {
color: "rgba(206, 79, 81, 0.2)"
}
},
{
value: [4000, 14000, 18000, 21000, 32000, 10000],
name: "关键和新型技术清单",
areaStyle: {
color: "rgba(250, 140, 22, 0.2)"
}
}
]
}
]
});
// 趋势图
const trendOption = ref({
tooltip: {
trigger: "axis",
axisPointer: {
type: "shadow"
}
},
grid: {
top: "8%",
bottom: "5%",
left: "2%",
right: "2%",
containLabel: true
},
legend: {
// type: "scroll",
// show: true,
// orient: "horizontal",
icon: "circle",
data: ["量子科技", "人工智能", "通信网络", "集成电路", "生物科技", "能源领域"]
},
xAxis: {
type: "category",
data: ["2021", "2022", "2023", "2024", "2025"]
},
yAxis: {
type: "value"
},
series: [
{
name: "量子科技",
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(22, 119, 255, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(22, 119, 255, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
},
{
name: "人工智能",
data: [20, 230, 100, 100, 30, 210, 180],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(206, 79, 81, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(206, 79, 81, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
},
{
name: "通信网络",
data: [150, 100, 200, 130, 50, 190, 330],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(255, 197, 61, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(255, 197, 61, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
},
{
name: "集成电路",
data: [220, 100, 250, 180, 170, 100, 80],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(255, 204, 199, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(255, 204, 199, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
},
{
name: "生物科技",
data: [220, 300, 50, 180, 170, 210, 400],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(179, 127, 235, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(179, 127, 235, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
},
{
name: "能源领域",
data: [120, 200, 150, 80, 70, 110, 130],
type: "bar",
barWidth: 12,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(127, 218, 235, 1)" },
// { offset: 0.5, color: '#188df0' },
{ offset: 1, color: "rgba(127, 214, 235, 0)" }
]),
borderRadius: [6, 6, 0, 0]
}
}
]
});
// 历次制裁过程
const sanctionProcessList = ref([
{
title: "2025年3月—SDN清单",
desc: "54家中国实体",
content:
"2025年3月25日,美国商务部工业与安全局以从事有悖于美国国家安全和外交政策利益的活动为由,宣布将来自中国的54家实体新增至“实体清单”。"
},
{
title: "2025年1月—SDN清单",
desc: "25家中国实体",
content:
"2025年1月15日,美国商务部工业和安全局宣布更新《出口管理条例》(EAR),并发布两项最终规则,将25家中国企业及其相关实体列入实体清单。"
},
{
title: "2025年12月—SDN清单",
desc: "12家中国实体",
content:
"2024年12月20日,美国商务部工业与安全局以从事有悖于美国国家安全和外交政策利益的活动为由,宣布将来自中国的12家实体新增至“商业管制清单”。"
},
{
title: "2024年10月—CMIC清单",
desc: "6家中国实体",
content: "2024年10月21日,美国商务部将6个中国实体加入其“实体清单”,同时将 1 个中国实体移出该清单。"
},
{
title: "2024年8月—SDN清单",
desc: "39家中国实体",
content: "2024年8月23日,美国商务部工业安全局发布公告,将多个外国实体加入其“实体清单”,其中包括39个中国实体。"
}
]);
// 进度条状态
const getStatus = _percent => {
const percent = _percent * 100;
if (percent >= 90) {
return "exception";
} else if (percent >= 50) {
return "warning";
} else {
return "success";
}
};
// 制裁实体
const currentPage = ref(1); // 默认显示第5页
const pageSize = ref(10);
const searchKeyword = ref("");
const strengthLabels = {
strong: "强",
medium: "中",
weak: "弱",
none: "无"
};
// 模拟数据
const mockData = [
{
name: "科大讯飞股份有限公司",
domains: ["人工智能"],
sanctionDate: "2025年9月",
strength: "strong",
revenue: "325",
listingLocation: "深圳"
},
{
name: "华为技术有限公司",
domains: ["通信网络", "集成电路"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "290",
listingLocation: ""
},
{
name: "中国航空工业集团",
domains: ["航空航天"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "288",
listingLocation: ""
},
{
name: "杭州海康威视数字技术股份有限公司",
domains: ["人工智能"],
sanctionDate: "2025年9月",
strength: "weak",
revenue: "203",
listingLocation: "深圳"
},
{
name: "浪潮集团有限公司",
domains: ["人工智能"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "195",
listingLocation: "香港"
},
{
name: "中芯国际集成电路制造有限公司",
domains: ["集成电路"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "190",
listingLocation: ""
},
{
name: "北京复旦微电子技术有限公司",
domains: ["集成电路"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "184",
listingLocation: ""
},
{
name: "哈尔滨工业大学",
domains: ["人工智能", "集成电路"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "无营收数据",
listingLocation: ""
},
{
name: "中兴通讯股份有限公司",
domains: ["通信网络", "集成电路"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "117",
listingLocation: "上海"
},
{
name: "大疆创新科技有限公司",
domains: ["航空航天"],
sanctionDate: "2025年9月",
strength: "medium",
revenue: "1",
listingLocation: "纽约"
}
];
// 生成更多数据以模拟1329家实体
const generateMoreData = () => {
const moreData = [];
const companySuffixes = ["有限公司", "集团", "股份有限公司", "技术有限公司", "研究所"];
const domainsList = [
["人工智能"],
["通信网络"],
["航空航天"],
["集成电路"],
["人工智能", "集成电路"],
["通信网络", "集成电路"],
["航空航天", "人工智能"]
];
const strengths = ["strong", "medium", "weak"];
for (let i = 0; i < 1318; i++) {
const nameSuffix = companySuffixes[Math.floor(Math.random() * companySuffixes.length)];
const companyName = `实体${i + 12}${nameSuffix}`;
const domains = domainsList[Math.floor(Math.random() * domainsList.length)];
const strength = strengths[Math.floor(Math.random() * strengths.length)];
const revenue = Math.floor(Math.random() * 400) + 1;
moreData.push({
name: companyName,
domains: domains,
sanctionDate: "2025年9月",
strength: strength,
revenue: revenue.toString()
});
}
return moreData;
};
const allData = [...mockData, ...generateMoreData()];
// 计算属性
const total = computed(() => allData.length);
const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
const filteredData = computed(() => {
if (!searchKeyword.value) return allData;
const keyword = searchKeyword.value.toLowerCase();
return allData.filter(
item => item.name.toLowerCase().includes(keyword) || item.domains.some(domain => domain.toLowerCase().includes(keyword))
);
});
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return filteredData.value.slice(start, end);
});
// 当前法案
const curBill = ref({
billName: "",
billId: 0,
dyqkList: [],
hylyList: [],
tarName: "",
introductionDate: ""
});
const warningList = ref([
{
title: "兰德智库发布2025中国年度科技报告",
time: "一天前",
status: "特别重大"
},
{
title: "信息技术与创新基金会发布《中国正在迅速成...",
time: "一天前",
status: "特别重大"
},
{
title: "首次提出“限制外国敏感实体获取补贴",
time: "一天前",
status: "重大风险"
},
{
title: "将中国企业海外子公司、合资公司纳入受...",
time: "一天前",
status: "重大风险"
},
{
title: "H.R.8333《生物安全法案》将华大基因等...",
time: "一天前",
status: "一般风险"
}
]);
const curBillList = ref([
{
billName: "大而美法案",
introductionDate: "2025年7月4日",
img: bill1
},
{
billName: "GENIUS稳定币法案",
introductionDate: "2025年7月5日",
img: bill2
},
{
billName: "美越贸易协议",
introductionDate: "2025年7月6日",
img: bill3
},
{
billName: "美越贸易协议",
introductionDate: "2025年7月7日",
img: bill4
},
{
billName: "汽车零部件25%关税实施规则",
introductionDate: "2025年7月10日",
img: bill5
},
{
billName: "汽车零部件25%关税实施规则",
introductionDate: "2025年7月12日",
img: bill6
},
{
billName: "小额豁免包裹政策调整",
introductionDate: "2025年7月14日",
img: bill7
},
{
billName: "NIH预算否决案",
introductionDate: "2025年7月15日",
img: bill8
},
{
billName: "得州国会选区重划法案",
introductionDate: "2025年7月17日",
img: bill9
},
{
billName: "美越贸易协议",
introductionDate: "2025年7月24日",
img: bill10
},
{
billName: "美越贸易协议",
introductionDate: "2025年8月4日",
img: bill11
},
{
billName: "美越贸易协议",
introductionDate: "2025年8月8日",
img: bill12
}
]);
const releaseTime = ref("近一年发布");
const releaseTimeList = ref([
{
label: "近半年发布",
value: "近半年发布"
},
{
label: "近一年发布",
value: "近一年发布"
},
{
label: "近两年发布",
value: "近两年发布"
},
{
label: "近三年发布",
value: "近三年发布"
},
{
label: "近五年发布",
value: "近五年发布"
}
]);
const categoryList = ref([
// "全部分类",
// "生物科技",
// "集成电路",
// "通信网络",
// "量子科技",
// "新能源",
// "新一代信息技术",
// "海洋",
// "先进制造",
// "新材料",
// "航空航天",
]);
const activeCate = ref("全部分类");
const activeHylyId = ref("");
// 获取领域分类
const handleGetHylyList = async () => {
try {
const res = await getHylyList();
console.log("行业领域列表");
categoryList.value = res.data;
const obj = {
id: 0,
hylyid: "",
hylymc: "全部分类"
};
categoryList.value = [obj, ...categoryList.value];
} catch (error) {}
};
const chart1Data = ref({
title: [
"2024-09",
"2024-10",
"2024-11",
"2024-12",
"2025-01",
"2025-02",
"2025-03",
"2025-04",
"2025-05",
"2025-06",
"2025-07",
"2025-08"
],
data: [
{
name: "提出法案",
value: [145, 52, 84, 99, 71, 96, 128, 144, 140, 168, 188, 172]
},
{
name: "通过法案",
value: [6, 3, 4, 6, 11, 5, 2, 14, 16, 27, 28, 44]
}
]
});
// 根据法案类型获取法案列表
const handleGetBillsByType = async () => {
const params = {
type: activeHylyId.value
};
try {
const res = await getBillsByType(params);
console.log("根据法案类型获取法案列表", res);
curBillList.value = res.data.map(item => {
return {
billId: item.billId,
billName: item.billName,
introductionDate: item.introductionDate,
img: bill1
};
});
} catch (error) {}
};
// 查看社交媒体详情
const handleToSocialDetail = item => {
const route = router.resolve({
path: "/characterPage",
query: {
personId: item.id
}
});
window.open(route.href, "_blank");
};
onMounted(async () => {
handleGetHylyList();
curBill.value = billList.value[0];
handleGetBillsByType();
let chart1 = getMultiLineChart(chart1Data.value.title, chart1Data.value.data[0].value, chart1Data.value.data[1].value);
setChart(chart1, "chart1");
});
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
box-shadow: none;
}
.home-header {
height: 64px;
background: url("@/assets/images/nav-bg.png");
box-sizing: border-box;
padding-left: 160px;
display: flex;
justify-content: space-between;
padding: 0 160px;
}
.box1 {
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
.box1-absolute {
position: absolute;
width: 240px;
height: 89px;
top: 30px;
right: -24px;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
padding-right: 50px;
box-sizing: border-box;
background: linear-gradient(to right, rgba(206, 79, 81, 0), rgba(206, 79, 81, 0.3));
&-des {
display: flex;
gap: 5px;
align-items: center;
font-size: 18px;
font-weight: 700;
color: rgba(206, 79, 81, 1);
}
&-num {
font-size: 32px;
font-weight: 700;
color: rgba(206, 79, 81, 1);
}
}
.box1-top {
// display: flex;
// flex-direction: column;
// gap: 10px;
padding-left: 30px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
&-title {
font-size: 20px;
font-weight: 700;
color: $base-color;
margin-top: 10px;
margin-bottom: 15px;
}
&-content {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 20px;
&-item {
display: flex;
gap: 5px;
&-title {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
}
}
}
}
.box1-bottom {
padding-left: 30px;
&-title {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
margin-bottom: 15px;
}
&-content {
display: flex;
gap: 15px;
flex-wrap: wrap;
justify-content: space-between;
padding-left: 10px;
&-item {
display: flex;
align-items: center;
justify-content: flex-start;
width: 48%;
/* 留出2%的间距 */
// margin-bottom: 6px;
box-sizing: border-box;
gap: 10px;
&-img {
width: 24px;
height: 24px;
}
&-txt {
font-size: 16px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
}
}
}
}
.box1-right {
display: flex;
flex-direction: column;
gap: 10px;
.box1-right-title {
font-size: 20px;
font-weight: 700;
color: $base-color;
}
.box1-right-tags {
display: flex;
gap: 10px;
}
.box1-right-content {
color: rgba(59, 65, 75, 1);
font-size: 16px;
font-weight: 400;
line-height: 28px;
}
.box1-right-footer {
margin-top: auto;
display: flex;
justify-content: space-between;
align-items: center;
.box1-right-footer-time {
color: rgba(95, 101, 108, 1);
font-size: 14px;
font-weight: 400;
}
}
}
}
.box2-main {
height: 320px;
overflow-y: auto;
.box2-main-item {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
box-sizing: border-box;
padding-right: 3px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.itemLeftStatus1 {
color: rgba(82, 196, 26, 1) !important;
background: rgba(246, 255, 237, 1) !important;
}
.itemLeftStatus2 {
color: rgba(250, 140, 22, 1) !important;
background: rgba(255, 247, 230, 1) !important;
}
.item-left {
display: flex;
align-items: center;
width: 40px;
height: 40px;
padding: 5px;
border-radius: 100%;
background: rgba(255, 241, 240);
color: rgba(245, 34, 45, 1);
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: 400;
line-height: 14px;
box-sizing: border-box;
text-align: center;
flex-shrink: 0;
}
.item-right {
margin-left: 13px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
height: 47px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
.text {
font-family: Microsoft YaHei;
line-height: 47px;
width: 80%;
font-size: 16px;
font-weight: 400;
color: rgba(59, 65, 75, 1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.time {
margin-left: 10px;
line-height: 47px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
}
}
}
}
.box2-footer {
position: absolute;
left: 0;
right: 0;
bottom: 20px;
width: 461px;
height: 42px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
border-radius: 6px;
background: var(--color-main-active);
margin: 0 auto;
cursor: pointer;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 8px;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
}
}
.box3 {
display: flex;
justify-content: space-between;
align-items: flex-start;
.box3-content-title {
font-size: 18px;
font-weight: 700;
font-family: Microsoft YaHei;
width: 470px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(247, 248, 249, 1);
color: $base-color;
margin-bottom: 15px;
}
.box3-content {
.el-progress--line {
width: 82px;
}
}
}
.box4 {
.box4-item {
display: flex;
gap: 10px;
align-items: flex-start;
padding-bottom: 30px;
.box4-item-left {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 8px;
position: relative;
.box4-item-left-icon {
width: 10px;
height: 10px;
}
.box4-item-left-line {
width: 1px;
height: 145px;
position: absolute;
top: 15px;
border-left: 1px solid rgba(10, 87, 166, 0.3);
}
}
.box4-item-right {
display: flex;
flex-direction: column;
gap: 5px;
.box4-item-right-header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 12px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
&-title {
font-size: 18px;
color: $base-color;
font-weight: 700;
}
&-desc {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
}
}
.box4-item-right-content {
margin-top: 10px;
font-size: 16px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
text-overflow: ellipsis;
height: 70px;
line-height: 25px;
}
}
}
.box4-footer {
margin-top: 50px;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 30px;
left: 50%;
margin-left: -20px;
}
}
.box5 {
height: 115%;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
}
:deep(.table-header) {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
}
:deep(.table-row) {
height: 64px;
}
.domain-tags {
display: flex;
gap: 8px;
}
.box5-header-right {
font-size: 16px;
font-weight: 700;
color: $base-color;
}
.table-footer {
margin-top: 20px;
}
.home-wrapper {
width: 100%;
height: 100%;
position: relative;
overflow-y: hidden;
.home-main {
position: relative;
width: 100%;
height: 100%;
overflow-y: auto;
// background: url("./assets/images/background.png");
// background-size: 100% 100%;
.home-top-bg {
background:
url("./assets/images/background.png"),
linear-gradient(180deg, rgba(229, 241, 254, 1) 0%, rgba(246, 251, 255, 0) 30%);
background-size: 100% 100%;
position: absolute;
width: 100%;
height: 100%;
z-index: -100;
top: -64px;
}
.home-main-header {
margin-top: 43px;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
.home-main-header-top {
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
margin-top: 43px;
}
.home-main-header-footer {
margin-top: 38px;
width: 700px;
height: 64px;
box-sizing: border-box;
padding: 0 108px;
display: flex;
justify-content: space-between;
.home-main-header-footer-item {
padding: 0 10px;
text-align: center;
.item-top {
height: 22px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 36px;
font-weight: 700;
line-height: 22px;
}
.item-footer {
margin-top: 10px;
height: 30px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
}
}
}
.home-main-header-footer-link {
display: flex;
justify-content: center;
padding: 30px 0;
gap: 16px;
}
.home-main-header-footer-info {
width: 1600px;
display: flex;
justify-content: space-between;
padding: 30px 0;
.card {
box-sizing: border-box;
width: 792px;
height: 142px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.65);
position: relative;
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
cursor: pointer;
&:hover {
transform: translateY(-3px);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
.icon {
position: absolute;
width: 4px;
height: 111px;
top: 15px;
left: 0;
}
.title {
margin-top: 23px;
height: 31px;
margin-left: 30px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 24px;
font-weight: 700;
line-height: 32px;
letter-spacing: 0px;
text-align: left;
}
.content {
margin-top: 17px;
margin-left: 30px;
width: 731px;
height: 48px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.num {
height: 42px;
text-align: right;
position: absolute;
top: 21px;
right: 31px;
font-family: Microsoft YaHei;
font-size: 32px;
font-weight: 700;
line-height: 42px;
letter-spacing: 0px;
}
}
}
.home-main-header-btn-box {
width: 688px;
margin: 0 auto;
margin-top: 39px;
display: flex;
justify-content: space-between;
.btn {
display: flex;
align-items: center;
gap: 9px;
width: 160px;
height: 48px;
border: 1px solid #aed6ff;
box-sizing: border-box;
border-radius: 24px;
background: #e7f3ff;
cursor: pointer;
position: relative;
&:hover {
background: #cae3fc;
}
.btn-text {
width: 80px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 400;
line-height: 48px;
margin-left: 36px;
text-align: center;
}
.btn-icon {
position: absolute;
top: 16px;
right: 19px;
width: 6px;
height: 12px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.home-main-center {
margin-top: 34px;
.center-top {
height: 450px;
display: flex;
gap: 20px;
.box1 {
display: flex;
gap: 10px;
position: relative;
// .box1-left {
// position: absolute;
// left: 0;
// top: 200px;
// width: 24px;
// height: 48px;
// background: #e7f1ff;
// display: flex;
// justify-content: center;
// align-items: center;
// cursor: pointer;
// .icon {
// width: 11px;
// height: 18px;
// img {
// width: 100%;
// height: 100%;
// }
// }
// }
.box1-right {
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
cursor: pointer;
.icon {
width: 11px;
height: 18px;
img {
width: 100%;
height: 100%;
}
}
}
.box1-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
justify-content: space-between;
.box1-header-left {
display: flex;
.icon {
width: 18px;
height: 18px;
margin-top: 19px;
img {
width: 100%;
height: 100%;
}
}
.title {
height: 22px;
margin-left: 18px;
margin-top: 16px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
}
.box1-header-right {
margin-top: 19px;
height: 16px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 16px;
cursor: pointer;
}
}
.box1-main {
display: flex;
height: 354px;
margin-top: 22px;
.box1-main-top {
height: 68px;
display: flex;
justify-content: space-between;
.box1-main-top-left {
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
.box1-main-top-right {
margin-left: 20px;
display: flex;
.num {
padding: 1px 8px;
height: 24px;
box-sizing: border-box;
border: 1px solid rgba(145, 202, 255, 1);
border-radius: 4px;
background: rgba(230, 244, 255, 1);
}
.tag {
box-sizing: border-box;
border: 1px solid rgba(135, 232, 222, 1);
border-radius: 4px;
background: rgba(230, 255, 251, 1);
}
}
}
}
}
.box2 {
flex: 1;
padding-right: 20px;
height: 450px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.box2-header {
height: 54px;
display: flex;
.icon {
width: 24px;
height: 22px;
margin-left: 33px;
margin-top: 18px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 22px;
margin-top: 16px;
height: 22px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
.num {
width: 24px;
height: 16px;
text-align: center;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 12px;
margin-left: 6px;
margin-top: 17px;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 100px;
background: rgba(255, 77, 79, 1);
}
.more {
margin-top: 19px;
margin-left: 256px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 16px;
}
}
.box2-main {
height: 282px;
overflow-y: auto;
.box2-main-item {
margin-left: 23px;
height: 47px;
width: 464px;
display: flex;
.itemLeftStatus1 {
color: rgba(82, 196, 26, 1) !important;
background: rgba(246, 255, 237, 1) !important;
}
.itemLeftStatus2 {
color: rgba(250, 140, 22, 1) !important;
background: rgba(255, 247, 230, 1) !important;
}
.item-left {
margin-top: 4px;
margin-left: 2px;
width: 40px;
height: 40px;
border-radius: 20px;
background: rgba(255, 241, 240);
color: rgba(245, 34, 45, 1);
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: 400;
line-height: 14px;
box-sizing: border-box;
padding: 6px 4px;
text-align: center;
}
.item-right {
margin-left: 13px;
width: 408px;
height: 47px;
border-top: 1px solid rgba(240, 242, 244, 1);
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
.text {
width: 348px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 47px;
}
.time {
margin-left: 10px;
line-height: 47px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
}
}
}
}
.box2-footer {
position: absolute;
left: 26px;
bottom: 20px;
width: 430px;
height: 42px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
border-radius: 6px;
background: rgba(22, 119, 255, 1);
cursor: pointer;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 8px;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
}
.center-footer {
margin-top: 21px;
height: 450px;
display: flex;
.box3 {
width: 900px;
height: 450px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.box3-header {
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
.box3-header-left {
display: flex;
.box3-header-icon {
margin-top: 15px;
width: 13px;
height: 13px;
img {
width: 100%;
height: 100%;
}
}
.box3-header-title {
margin-top: 16px;
margin-left: 22px;
height: 22px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
}
.box3-header-right {
display: flex;
justify-content: flex-end;
width: 178px;
height: 22px;
.right-box {
display: flex;
margin-top: 16px;
width: 89px;
height: 22px;
justify-content: flex-end;
.icon1 {
margin-top: 5px;
width: 12px;
height: 12px;
border-radius: 6px;
background: rgba(20, 89, 187, 1);
}
.icon2 {
margin-top: 5px;
width: 12px;
height: 12px;
border-radius: 6px;
background: rgba(250, 140, 22, 1);
}
.text {
margin-left: 5px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
}
.box3-main {
height: 397px;
}
}
.box4 {
margin-left: 20px;
width: 521px;
height: 450px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.box4-header {
width: 452px;
margin: 0 auto;
height: 53px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
.header-icon {
margin-top: 18px;
width: 18px;
height: 18px;
img {
width: 100%;
height: 100%;
}
}
.header-title {
margin-top: 16px;
margin-left: 26px;
height: 22px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
}
.box4-main {
width: 452px;
margin: 0 auto;
margin-top: 8px;
height: 360px;
overflow-y: auto;
.box4-main-item {
margin-top: 6px;
height: 30px;
display: flex;
.leftStatus3 {
color: rgba(255, 197, 61, 1) !important;
}
.leftStatus2 {
color: rgba(255, 169, 64, 1) !important;
}
.left {
width: 44px;
text-align: left;
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 30px;
color: rgba(206, 79, 81, 1);
}
.center {
width: 300px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
}
.right {
width: 108px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 30px;
text-align: right;
}
}
}
}
}
}
.home-main-footer {
// width: 100%;
height: 911px;
background: rgba(248, 249, 250, 1);
.home-main-footer-header {
margin-top: 37px;
margin-bottom: 36px;
// width: 1600px;
height: 42px;
// background: orange;
display: flex;
justify-content: space-between;
.btn-box {
width: 1300px;
display: flex;
justify-content: space-between;
.btn {
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 42px;
padding: 0 24px;
border-radius: 21px;
background: rgba(20, 89, 187, 0);
cursor: pointer;
&:hover {
background: rgba(20, 89, 187, 0.1);
}
}
.btnActive {
padding: 0 24px;
border-radius: 21px;
background: rgba(20, 89, 187, 1);
color: #fff;
&:hover {
color: #fff;
background: rgba(20, 89, 187, 1);
}
}
}
.select-box {
height: 42px;
box-sizing: border-box;
padding: 5px 0;
}
}
.home-main-footer-main {
width: 100%;
// background: orange;
display: flex;
flex-wrap: wrap;
// justify-content: space-between;
// justify-content: center;
.main-item {
width: 240px;
height: 320px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: #fff;
margin-bottom: 24px;
margin-right: 25px;
.main-item-box1 {
margin-top: 20px;
margin-left: 45px;
width: 150px;
height: 200px;
box-sizing: border-box;
border: 1px solid rgba(240, 242, 244, 1);
img {
width: 100%;
height: 100%;
}
}
.main-item-box2 {
margin-top: 26px;
text-align: center;
height: 30px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 30px;
}
.main-item-box3 {
text-align: center;
height: 30px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
}
}
}
}
}
}
:deep(.el-input__wrapper) {
box-shadow: none;
border-radius: 10px;
}
:deep(.el-input__wrapper) {
box-shadow: none;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
:deep(.el-table thead) {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
:deep(.el-table tr) {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.center-center {
width: 1604px;
margin: 0 auto;
margin-top: 21px;
height: 450px;
display: flex;
.center-center-news {
flex-shrink: 0;
/* 保证内部 NewsList 的滚动条与外层 box3 右侧对齐 */
:deep(.box3-main) {
width: 100%;
box-sizing: border-box;
}
}
.boxs4 {
margin-left: 20px;
width: 792px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(25, 69, 130, 0.2);
background: rgba(255, 255, 255, 1);
}
}
</style>
<template>
<div class="page-container">
<div style="height: 30px; width: 960px; display: flex; justify-content: flex-start">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item>国家科技安全</el-breadcrumb-item>
<el-breadcrumb-item>中美博弈概览</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/' }"
><span style="font-weight: 700; color: rgba(6, 110, 217, 1)"
>美对华科技合作限制信息平台</span
></el-breadcrumb-item
>
</el-breadcrumb>
</div>
<el-input v-model="searchKey" style="max-width: 960px" placeholder="搜索政策关键词" class="input-with-select">
<template #append>
<el-button :icon="Search" size="large" type="primary">搜索</el-button>
</template>
</el-input>
<div class="page-data">
<div class="page-data-item" v-for="item in pageData" :key="item.desc">
<div class="page-data-item-num">
{{ item.num }}
</div>
<div class="page-data-item-desc">
{{ item.desc }}
</div>
</div>
</div>
<!-- 内容 -->
<div class="page-content">
<el-row :gutter="20">
<el-col :span="16">
<custom-container title="本次制裁共新增83个实体,其中53个中国大陆实体、1个中国台湾实体。" height="866px">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-law.png" alt="" />
<span class="custom-container-title">最新限制动态</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>查看详情 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-card" v-for="item in limitData" :key="item.title">
<div class="content-card-date">{{ `${item.date} · ${item.importance}` }}</div>
<div class="content-card-title">
<div class="content-card-title-text">{{ item.title }}</div>
<!-- <div class="content-card-title-tag" v-for="tag in item.tag" :key="tag">{{ tag }}</div> -->
<el-tag :type="tagType[Math.floor(Math.random() * 5)]" v-for="tag in item.tag" :key="tag">{{
tag
}}</el-tag>
</div>
<div class="content-card-desc">{{ item.desc }}</div>
</div>
</template>
<!-- 底部自定义内容 -->
<!-- <template #footer>
<el-button>取消</el-button>
<el-button type="primary">保存更改</el-button>
</template> -->
</custom-container>
</el-col>
<el-col :span="8">
<custom-container title="风险信号" height="450px">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-warning.png" alt="" />
<span class="custom-container-title">风险信号</span>
<span class="custom-container-count">5</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>更多 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-list-card" v-for="item in riskData" :key="item.title">
<el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag>
<div class="content-list">
<div class="content-list-title">{{ item.title }}</div>
<div class="content-list-time">{{ item.date }}</div>
</div>
</div>
<div class="content-list-footer">
<el-button type="primary" size="default" style="width: 100%">
<img class="icon-mark" src="@/assets/images/icon-mark.png" alt="" />
风险处理
</el-button>
</div>
</template>
</custom-container>
<custom-container title="学术交流限制" height="400px">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-hot.png" alt="" />
<span class="custom-container-title">学术交流限制</span>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-list-card" v-for="item in academicData" :key="item.title">
<!-- <el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag> -->
<div class="content-list-row">
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="政策禁令限制">
<template #header-left>
<div class="custom-container-header-left1">
<div class="icon"></div>
<div class="title">学术交流限制</div>
</div>
</template>
<template #default>
<div
class="content-list-item content-card-title-text-2"
v-for="item in policyBanData"
:key="item.title"
>
<!-- <el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag> -->
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
<div class="content-card-tag-box">
<div class="content-card-tag-box-item">
<el-tag
:type="tagType[Math.floor(Math.random() * 5)]"
v-for="tag in item.tag"
:key="tag"
>{{ tag }}</el-tag
>
</div>
<el-button class="content-card-tag-btn" type="primary" link>查看全文 ></el-button>
</div>
</div>
<div class="content-card-footer">
<el-button type="primary" link>
查看详情 <img class="icon-mark" src="@/assets/images/icon-double-down.png" alt="" />
</el-button>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="近期时间线">
<template #header-left>
<div class="custom-container-header-left1">
<div class="icon"></div>
<div class="title">近期时间线</div>
</div>
</template>
<template #default>
<div style="text-align: center; width: 100%; margin-bottom: 20px">
<el-space>
<el-button plain type="primary">全部</el-button>
<el-button>半导体</el-button>
<el-button>人工智能</el-button>
<el-button>出口管制</el-button>
<el-button>学术交流</el-button>
<el-button>投资审查</el-button>
</el-space>
</div>
<div class="time-line" v-for="item in recentTimeline" :key="item.title">
<img class="time-line-dot" src="@/assets/images/dot.png" alt="" />
<div class="time-line-title">
<span class="time-line-title-text">{{ item.date }}</span>
<div class="time-line-desc">
<div class="time-line-desc-title">{{ item.title }}</div>
<div class="content-card-tag-box-item">
<el-tag
:type="tagType[Math.floor(Math.random() * 5)]"
v-for="tag in item.tag"
:key="tag"
>{{ tag }}</el-tag
>
</div>
</div>
</div>
</div>
<div class="content-card-footer">
<el-button type="primary" link>
查看详情 <img class="icon-mark" src="@/assets/images/icon-double-down.png" alt="" />
</el-button>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="科研项目合作情况">
<template #header-left>
<div class="custom-container-header-left1">
<div class="icon"></div>
<div class="title">科研项目合作情况</div>
</div>
</template>
<template #default>
<div
class="content-list-item content-card-title-text-2"
v-for="item in researchData"
:key="item.title"
>
<!-- <el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag> -->
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
</div>
<div class="content-card-footer">
<el-button type="primary" link>
查看更多 <img class="icon-mark" src="@/assets/images/icon-double-down.png" alt="" />
</el-button>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="人才流动限制情况">
<template #header-left>
<div class="custom-container-header-left1">
<div class="icon"></div>
<div class="title">人才流动限制情况</div>
</div>
</template>
<template #default>
<div class="content-list-item content-card-title-text-2" v-for="item in talentData" :key="item.title">
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
</div>
<div class="content-card-footer">
<el-button type="primary" link>
查看更多 <img class="icon-mark" src="@/assets/images/icon-double-down.png" alt="" />
</el-button>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ArrowRight } from "@element-plus/icons-vue";
import CustomContainer from "@/components/Container/index.vue";
const searchKey = ref("");
const pageData = ref([
{
num: 42,
desc: "生效政策禁令"
},
{
num: 128,
desc: "受限制实体"
},
{
num: 16,
desc: "受限制领域"
},
{
num: 7,
desc: "本月更新"
}
]);
// tag类型
const tagType = ["primary", "success", "info", "warning", "danger"];
// 限制动态数据
const limitData = ref([
{
date: "2025年9月2日",
importance: "紧急",
tag: ["半导体", "出口管制"],
title: "美国撤销多家芯片企业在华VEU授权",
desc: `美国最终用户审查委员会(ERC)宣布撤销英特尔、SK海力士和三星在中国工厂的"经验证最终用户"(VEU)授权,影响其在华芯片生产。新规定计划于10月2日《联邦公报》出版120天后正式生效:cite[3]:cite[4]。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 半导体", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 半导体", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
}
]);
// 风险
const riskType = ref(["特别重大", "重大风险", "一般风险"]);
const riskData = ref([
{
date: "一天前",
importance: "特别重大",
danger: "danger",
title: "美国大而美法案落地,总统签署通过"
},
{
date: "两天前",
importance: "重大风险",
danger: "warning",
title: "美大而美法案7月1日以51:50的票数通过..."
},
{
date: "三天前",
importance: "特别重大",
danger: "danger",
title: "首次提出“限制外国敏感实体获取补贴”"
},
{
date: "四天前",
importance: "一般风险",
danger: "success",
title: "将中国企业海外子公司、合资公司纳入受..."
},
{
date: "五天前",
importance: "一般风险",
danger: "success",
title: "H.R.8333《生物安全法案》将华大基因等..."
}
]);
// 学术交流限制
const academicData = ref([
{
title: "签证限制",
desc: "美国国务院已开始取消部分中国留学生的签证,针对STEM领域学生和研究人员加强审查。"
},
{
title: "合作项目终止",
desc: "密歇根大学终止了与上海交通大学的长期合作,佐治亚理工停止深圳学院运营。"
},
{
title: "数据访问限制",
desc: "美国国立卫生研究院(NIH)禁止中国、俄罗斯、伊朗等国的机构访问其受控数据。"
}
]);
// 政策禁令限制
const policyBanData = ref([
{
title: "《美国优先投资政策》总统备忘录",
desc: `2025年2月签署,限制美国资本投资中国"军民融合"战略相关产业,并审查甚至阻止中国对美关键领域的投资。备忘录扩大CFIUS的审查范围,要求将限制领域扩展到"医疗、农业、能源、原材料等战略行业"。`,
tag: ["投资审查", "国家暗安全"]
},
{
title: "《外国公司问责法》",
desc: `2025年2月签署,限制美国资本投资中国"军民融合"战略相关产业,并审查甚至阻止中国对美关键领域的投资。备忘录扩大CFIUS的审查范围,要求将限制领域扩展到"医疗、农业、能源、原材料等战略行业"。`,
tag: ["金融", "上市公司"]
}
]);
// 近期时间线
const recentTimeline = ref([
{
date: "2025-9-2",
tag: ["半导体", "出口管制"],
title: "撤销芯片企业VEU授权"
},
{
date: "2025-07-29",
tag: ["人工智能", "出口管制"],
title: "强化AI芯片出口限制"
},
{
date: "2025-05-08",
tag: ["半导体", "出口管制"],
title: "TikTok剥离期限再次延期"
}
// {
// date: "2025-04-02",
// tag: ["半导体", "出口管制"],
// title: "14117规则生效"
// }
]);
// 科研项目合作
const researchData = ref([
{
title: "合作范围缩小",
desc: "中美科技合作协定虽得以续签,但范围有所收窄,更强调透明性、互惠性和研究人员保护。"
},
{
title: "敏感领域合作遇冷",
desc: `在人工智能、量子计算等敏感技术领域,合作项目面临严格的"国家安全审查"。`
}
]);
// 人才流动限制
const talentData = ref([
{
title: "华人科学家离美趋势",
desc: "2010年至2021年间,有近1.25万名华裔科学家离开美国回到中国。"
},
{
title: "审查措施加强",
desc: `美国政府以"国家安全"为由,调查并限制华人科学家参与美国科研项目。`
}
]);
</script>
<style lang="scss" scoped>
.page-container {
display: flex;
flex-direction: column;
align-items: center;
/* height: 100vh; */
margin: 0px auto;
padding: 0px 120px;
padding-top: 40px;
/* background-color: rgba(234, 236, 238, 1); */
background: url("/assets/images/background.png");
}
.page-data {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
width: 960px;
gap: 80px;
}
.page-data-item {
display: flex;
flex-direction: column;
align-items: center;
}
.page-data-item-num {
font-size: 24px;
font-weight: 600;
color: var(--color-main-active);
}
.page-data-item-desc {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.page-content {
margin-top: 20px;
width: 100%;
/* padding: 0px 20px; */
}
.custom-container-header-left1 {
display: flex;
margin-left: -15px;
height: 24px;
.icon {
width: 8px;
height: 16px;
background: var(--color-main-active);
margin-top: 4px;
border-radius: 0 2px 2px 0;
}
.title {
margin-left: 14px;
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
}
.custom-container-header-left {
display: flex;
align-items: center;
gap: 8px;
padding-left: 20px;
padding-bottom: 5px;
/* border-bottom: 1px solid rgba(234, 236, 238, 1); */
}
.custom-container-title {
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
}
.custom-container-count {
text-align: center;
width: 24px;
height: 16px;
border-radius: 100px;
font-size: 12px;
background-color: rgba(255, 77, 79, 1);
color: #fff;
}
.custom-container-icon {
height: 18px;
width: 18px;
}
.content-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 8px;
padding: 12px 20px;
background-color: rgba(247, 248, 249, 1);
border-radius: 8px;
margin-bottom: 14px;
}
.content-card-date {
font-size: 14px;
font-weight: 400;
color: var(--color-main-active);
}
.content-card-title {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-title-text {
font-size: 16px;
font-weight: 700;
color: rgba(10, 18, 30, 1);
margin-right: auto;
}
.content-card-title-text-2 {
display: flex;
flex-direction: column;
gap: 11px;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
/* box-shadow: 0 2px 8px rgba(60, 87, 126, 0.2); */
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
}
.content-card-tag-box {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-tag-box-item {
display: flex;
align-items: center;
gap: 6px;
}
.content-card-tag-btn {
margin-left: autp;
}
.content-card-title-tag {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.content-card-desc {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.content-list-card {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-list {
height: 40px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-row {
height: 60px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-title {
width: 80%;
font-size: 16px;
font-weight: 400;
color: rgba(59, 65, 75, 1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.content-list-time {
font-size: 14px;
color: rgba(132, 136, 142, 1);
}
.content-list-footer {
width: 100%;
margin-top: 115px;
}
.icon-mark {
height: 12px;
width: 12px;
margin-right: 5px;
margin-left: 8px;
}
.content-card-footer {
display: flex;
width: 100%;
justify-content: center;
margin-top: 30px;
}
.time-line {
display: flex;
gap: 15px;
align-items: flex-start;
padding-left: 10px;
margin-bottom: 20px;
}
.time-line-title {
display: flex;
align-items: flex-start;
flex-direction: column;
gap: 8px;
width: 100%;
}
.time-line-dot {
width: 10px;
height: 10px;
margin-top: 5px;
}
.time-line-title-text {
font-size: 14px;
font-weight: 700;
color: rgba(95, 101, 108, 1);
color: var(--base-color);
}
.time-line-desc {
width: 100%;
display: flex;
justify-content: space-between;
}
.time-line-desc-title {
font-size: 16px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
margin-bottom: 5px;
}
.time-line-desc-text {
font-size: 14px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
}
.time-line-dot {
width: 8px;
height: 8px;
background-color: var(--color-main-active);
border-radius: 50%;
}
</style>
<template>
<div class="page-container">
<div style="height: 30px; width: 960px; display: flex; justify-content: flex-start">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item>国家科技安全</el-breadcrumb-item>
<el-breadcrumb-item>中美博弈概览</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/' }">科研资助体系</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-input v-model="searchKey" style="max-width: 960px" placeholder="搜索政策关键词" class="input-with-select">
<template #append>
<el-button :icon="Search" size="large" type="primary">搜索</el-button>
</template>
</el-input>
<div class="page-data">
<div class="page-data-item" v-for="item in pageData" :key="item.desc">
<div class="page-data-item-num">
{{ item.num }}
</div>
<div class="page-data-item-desc">
{{ item.desc }}
</div>
</div>
</div>
<div class="page-content">
<el-row :gutter="20">
<el-col :span="16">
<custom-container title="美国对华规则限制动态" height="450px">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-law.png" alt="" />
<span class="custom-container-title">美国联邦科研决策和资助体系</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>查看详情 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div ref="treeRef" style="height: 340px; width: 100%"></div>
</template>
</custom-container>
</el-col>
<el-col :span="8">
<custom-container title="风险信号" height="450px">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-warning.png" alt="" />
<span class="custom-container-title">风险信号</span>
<span class="custom-container-count">5</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>更多 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-list-card" v-for="item in riskData" :key="item.title">
<el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag>
<div class="content-list">
<div class="content-list-title">{{ item.title }}</div>
<div class="content-list-time">{{ item.date }}</div>
</div>
</div>
<div class="content-list-footer">
<el-button type="primary" size="default" style="width: 100%">
<img class="icon-mark" src="@/assets/images/icon-mark.png" alt="" />
风险处理
</el-button>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<custom-container title="科技领域重点布局" height="520px">
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>查看详情 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-chart">
<div class="content-chart-radio">
<el-radio-group v-model="radio" size="small">
<el-radio label="1">人工智能</el-radio>
<el-radio label="2">量子技术</el-radio>
<el-radio label="2">半导体</el-radio>
<el-radio label="2">生物技术</el-radio>
<el-radio label="2">能源技术</el-radio>
</el-radio-group>
</div>
<div class="content-chart-wrapper">
<el-row :gutter="20">
<el-col :span="10">
<div ref="pieRef" style="height: 400px; width: 100%"></div>
<!-- <ECharts ref="pieRef" :option="pieOption" autoresize :style="{ height: '326px' }" /> -->
</el-col>
<el-col :span="14">
<div class="content-list-item" v-for="item in policyBanData" :key="item.title">
<div class="content-card-title-text-2">
<!-- <el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag> -->
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
<div class="content-card-tag-box">
<div class="content-card-tag-box-item">
<el-tag :type="tagType[Math.floor(Math.random() * 5)]" v-for="tag in item.tag" :key="tag">{{
tag
}}</el-tag>
</div>
<!-- <el-button class="content-card-tag-btn" type="primary" link>查看全文 ></el-button> -->
</div>
</div>
<div class="content-list-item-money">
{{ item.money }}
</div>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
</custom-container>
</el-row>
<el-row :gutter="20">
<el-image :src="BGC" style="height: 429px; width: 100%"></el-image>
</el-row>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ArrowRight } from "@element-plus/icons-vue";
import ECharts from "@/components/Chart/index.vue";
import CustomContainer from "@/components/Container/index.vue";
import { useEcharts } from "@/hooks/useEcharts";
import echarts from "@/plugins/echarts";
import { treeData } from "./mock";
import { TreeChart } from "echarts/charts"; // 树图组件
import BGC from "@/assets/images/bgc-mock.png";
// 导入必要的组件
import { TitleComponent, TooltipComponent, LegendComponent } from "echarts/components";
// 导入渲染器(Canvas或SVG)
import { CanvasRenderer } from "echarts/renderers";
// 2. 注册所有需要的组件
echarts.use([
TreeChart, // 树图图表
TitleComponent, // 标题组件
TooltipComponent, // 提示框组件
LegendComponent, // 图例组件
CanvasRenderer // 渲染器:cite[7]
]);
const searchKey = ref("");
const pageData = ref([
{
num: 310,
desc: "生效政策禁令"
},
{
num: 14433,
desc: "受限制实体"
},
{
num: 126,
desc: "受限制领域"
},
{
num: 16,
desc: "本月更新"
}
]);
const riskData = ref([
{
date: "一天前",
importance: "特别重大",
danger: "danger",
title: "美国大而美法案落地,总统签署通过"
},
{
date: "两天前",
importance: "重大风险",
danger: "warning",
title: "美大而美法案7月1日以51:50的票数通过..."
},
{
date: "三天前",
importance: "特别重大",
danger: "danger",
title: "首次提出“限制外国敏感实体获取补贴”"
},
{
date: "四天前",
importance: "一般风险",
danger: "success",
title: "将中国企业海外子公司、合资公司纳入受..."
},
{
date: "五天前",
importance: "一般风险",
danger: "success",
title: "H.R.8333《生物安全法案》将华大基因等..."
}
]);
const treeRef = ref(null);
const pieRef = ref(null);
const treeOption = {
tooltip: {
trigger: "item",
triggerOn: "mousemove"
},
series: [
{
type: "tree",
data: [treeData],
top: "1%",
left: "7%",
bottom: "1%",
right: "20%",
symbolSize: 7,
label: {
position: "left",
verticalAlign: "middle",
align: "right",
fontSize: 9
},
leaves: {
label: {
position: "right",
verticalAlign: "middle",
align: "left"
}
},
emphasis: {
focus: "descendant"
},
expandAndCollapse: true,
animationDuration: 550,
animationDurationUpdate: 750
}
]
};
const pieOption = ref(null);
onMounted(() => {
const treeChart = echarts.init(treeRef.value);
const pieChart = echarts.init(pieRef.value);
// tree.setOption(treeOption);
pieOption.value = {
title: {
text: "",
left: "center",
textStyle: {
color: "#999",
fontWeight: "normal",
fontSize: 14
}
},
series: {
type: "pie",
radius: [70, 90],
top: -10 + "%",
// height: '33.33%',
left: "center",
width: 500,
itemStyle: {
borderColor: "#fff",
borderWidth: 1
},
label: {
alignTo: "edge",
// formatter: "{name|{b}}\n{time|{c} 小时}",
formatter: "{name|{b}}\n",
minMargin: 20,
edgeDistance: 0,
lineHeight: 15,
rich: {
time: {
fontSize: 10,
color: "#999"
}
}
},
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
},
labelLayout: function (params) {
const isLeft = params.labelRect.x < pieChart.getWidth() / 2;
const points = params.labelLinePoints;
// Update the end point.
points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
return {
labelLinePoints: points
};
},
data: [
{ name: "专家系统", value: 5.6 },
{ name: "机器人技术", value: 1 },
{ name: "自动驾驶", value: 0.8 },
{ name: "机器学习", value: 0.5 },
{ name: "自然语言处理", value: 0.5 },
{ name: "计算机视觉", value: 0.5 },
{ name: "其它", value: 3.8 }
]
}
};
console.log("树形结构数据 =>", treeOption, treeRef.value);
useEcharts(treeChart, treeOption);
useEcharts(pieChart, pieOption.value);
});
// tag类型
const tagType = ["primary", "success", "info", "warning", "danger"];
const policyBanData = ref([
{
title: "《美国优先投资政策》总统备忘录",
desc: `2025年2月签署,限制美国资本投资中国"军民融合"战略相关产业,并审查甚至阻止中国对美关键领域的投资。备忘录扩大CFIUS的审查范围,要求将限制领域扩展到"医疗、农业、能源、原材料等战略行业"。`,
tag: ["投资审查", "国家暗安全"],
money: "$125M"
},
{
title: "《外国公司问责法》",
desc: `2025年2月签署,限制美国资本投资中国"军民融合"战略相关产业,并审查甚至阻止中国对美关键领域的投资。备忘录扩大CFIUS的审查范围,要求将限制领域扩展到"医疗、农业、能源、原材料等战略行业"。`,
tag: ["金融", "上市公司"],
money: "$85M"
},
{
title: "《外国公司问责法》",
desc: `2025年2月签署,限制美国资本投资中国"军民融合"战略相关产业,并审查甚至阻止中国对美关键领域的投资。备忘录扩大CFIUS的审查范围,要求将限制领域扩展到"医疗、农业、能源、原材料等战略行业"。`,
tag: ["金融", "上市公司"],
money: "$69M"
}
]);
</script>
<style scoped>
.page-container {
display: flex;
flex-direction: column;
align-items: center;
/* height: 100vh; */
margin: 0px auto;
padding: 0px 120px;
padding-top: 40px;
background-color: rgba(234, 236, 238, 1);
}
.page-data {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
width: 960px;
gap: 80px;
}
.page-data-item {
display: flex;
flex-direction: column;
align-items: center;
}
.page-data-item-num {
font-size: 24px;
font-weight: 600;
color: var(--base-color);
}
.page-data-item-desc {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.page-content {
margin-top: 20px;
width: 100%;
/* padding: 0px 20px; */
}
.content-chart-wrapper {
height: 550px;
}
.custom-container-header-left {
display: flex;
align-items: center;
gap: 8px;
padding-left: 20px;
padding-bottom: 5px;
/* border-bottom: 1px solid rgba(234, 236, 238, 1); */
}
.custom-container-title {
font-size: 16px;
font-weight: 600;
color: var(--base-color);
}
.custom-container-count {
text-align: center;
width: 24px;
height: 16px;
border-radius: 100px;
font-size: 12px;
background-color: rgba(255, 77, 79, 1);
color: #fff;
}
.custom-container-icon {
height: 18px;
width: 18px;
}
.content-list-card {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-list {
height: 40px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-row {
height: 60px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-title {
width: 80%;
font-size: 16px;
font-weight: 400;
color: rgba(59, 65, 75, 1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.content-list-time {
font-size: 14px;
color: rgba(132, 136, 142, 1);
}
.content-list-footer {
width: 100%;
margin-top: 50px;
}
.icon-mark {
height: 12px;
width: 12px;
margin-right: 5px;
margin-left: 8px;
}
.content-list-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
/* box-shadow: 0 2px 8px rgba(60, 87, 126, 0.2); */
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
}
.content-list-item-money {
font-size: 20px;
font-weight: 700;
color: var(--color-main-active);
margin-left: 50px;
}
.content-card-title-text {
color: var(--color-main-active);
}
.content-card-desc {
font-size: 14px;
font-weight: 400;
color: rgba(132, 136, 142, 1);
}
.content-card-title-text-2 {
display: flex;
flex-direction: column;
gap: 11px;
}
.content-card-tag-box {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-tag-box-item {
display: flex;
align-items: center;
gap: 6px;
}
.content-card-tag-btn {
margin-left: autp;
}
.content-card-title-tag {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
</style>
export const treeData = {
name: "美国总统与国会",
children: [
{
name: "白宫科技政策办公室(OSTP)",
children: [
{
name: "国家科学技术委员会"
// children: [
// {
// name: "AgglomerativeCluster",
// value: 3938
// },
// {
// name: "CommunityStructure",
// value: 3812
// },
// {
// name: "HierarchicalCluster",
// value: 6714
// },
// {
// name: "MergeEdge",
// value: 743
// }
// ]
},
{
name: "总统科技顾问委员会"
// children: [
// {
// name: "BetweennessCentrality",
// value: 3534
// },
// {
// name: "LinkDistance",
// value: 5731
// },
// {
// name: "MaxFlowMinCut",
// value: 7840
// },
// {
// name: "ShortestPaths",
// value: 5914
// },
// {
// name: "SpanningTree",
// value: 3416
// }
// ]
}
// {
// name: "optimization",
// children: [
// {
// name: "AspectRatioBanker",
// value: 7074
// }
// ]
// }
]
},
{
name: "联邦资助机构",
children: [
{
name: "国防部(DOD)",
value: 17010
},
{
name: "国立卫生研究院(NIH)",
value: 5842
},
{
name: "国家科学基金会(NSF)"
// children: [
// {
// name: "ArrayInterpolator",
// value: 1983
// },
// {
// name: "ColorInterpolator",
// value: 2047
// },
// {
// name: "DateInterpolator",
// value: 1375
// },
// {
// name: "Interpolator",
// value: 8746
// },
// {
// name: "MatrixInterpolator",
// value: 2202
// },
// {
// name: "NumberInterpolator",
// value: 1382
// },
// {
// name: "ObjectInterpolator",
// value: 1629
// },
// {
// name: "PointInterpolator",
// value: 1675
// },
// {
// name: "RectangleInterpolator",
// value: 2042
// }
// ]
},
{
name: "能源部(DOE)",
value: 1041
},
{
name: "航空航天局(NASA)",
value: 5176
},
{
name: "农业部(USDA)",
value: 449
}
// {
// name: "Scheduler",
// value: 5593
// },
// {
// name: "Sequence",
// value: 5534
// },
// {
// name: "Transition",
// value: 9201
// },
// {
// name: "Transitioner",
// value: 19975
// },
// {
// name: "TransitionEvent",
// value: 1116
// },
// {
// name: "Tween",
// value: 6006
// }
]
}
]
};
<template>
<el-row :gutter="20">
<el-col :span="13">
<custom-container title="FCC简介">
<!-- 顶部右侧自定义内容 -->
<!-- <template #header-right>
<div style="display: flex; gap: 8px">
<el-input placeholder="搜索项目..." clearable style="width: 200px">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
<el-button type="primary">涉华背景</el-button>
<el-button>全部背景</el-button>
</div>
</template> -->
<!-- 中间内容自定义 -->
<template #default>
<div class="content-card">
<el-image class="content-img" :src="BISlogo" fit="fill" />
<div class="content-desc">
<div class="content-desc-item">
<div class="content-desc-title">机构名称:</div>
<div class="content-desc-content">美国联邦通信委员会(Federal Communications Commission,简称FCC)</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">成立年份:</div>
<div class="content-desc-content">1934年</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">机构职责:</div>
<div class="content-desc-content">监管通信业务,分配无线电频谱,制定通信标准,保护消费者权益</div>
</div>
<div class="content-desc-item">
<div class="content-desc-title">机构简介:</div>
<div class="content-desc-content content-desc-content-long">
美国联邦通信委员会(Federal Communications
Commission,简称FCC),是美国负责监管州际和国际通信的独立政府机构。其管辖范围包括无线电、电视、有线、卫星和电缆等通信方式。
FCC认证是美国对电子产品的强制性认证,主要确保产品的电磁兼容性和无线电频率安全性,防止设备对通信网络造成干扰。
</div>
</div>
</div>
</div>
</template>
</custom-container>
<custom-container title="规则限制时间线">
<!-- 中间内容自定义 -->
<template #default>
<div style="gap: 20px">
<div class="rule-time-line-item">2023年2月 - FCC发布FCC-22-84认证规则更新文件,限制Covered List清单上的设备</div>
<div class="rule-time-line-item">
2025年5月22日 - FCC全票通过新规,禁止被认定对美国国家安全构成威胁的中国实验室参与测试电子设备
</div>
<div class="rule-time-line-item">2025年6月 - 新规具体实施细则公布,明确实验室审查标准</div>
<div class="rule-time-line-item">2025年9月8日 - 部分条款正式生效执行</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="11">
<custom-container title="限制原因与背景">
<template #default>
<div class="content-vertical-card">
<div class="content-vertical-card-title-box">
<img class="content-vertical-card-title-img" src="@/assets/images/icon-air.png" fit="fill" />
<span class="content-vertical-card-title">数据对比</span>
</div>
<el-table :data="tableData" border style="width: 100%">
<el-table-column prop="region" label="地区" width="180" />
<el-table-column prop="num" label="数量" width="180" />
<el-table-column prop="proportion" label="占比" />
</el-table>
<div class="content-vertical-card-declaration">
根据FCC认可数据库显示,FCC声称约75%销往美国的电子产品依赖中国实验室进行测试
</div>
</div>
<div class="content-vertical-card">
<div class="content-vertical-card-title-box">
<img class="content-vertical-card-title-img" src="@/assets/images/icon-compair.png" fit="fill" />
<span class="content-vertical-card-title">数据对比官方理由</span>
</div>
<div class="content-vertical-card-desc-text">FCC以"国家安全"为由,声称:</div>
<div class="content-vertical-card-desc-text">· 部分中国实验室与中国政府、军方或国有企业存在关联</div>
<div class="content-vertical-card-desc-text">· 可能成为"外国对手渗透美国通信基础设施的入口"</div>
<div class="content-vertical-card-desc-text">· 需要防止"不安全设备进入美国市场"</div>
<div class="content-vertical-card-desc-emphasize">
FCC审查后认为,部分获认可的实验室与中国政府存在"深层联系",包括与国有企业或中国军方相关联的机构
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="16">
<custom-container title="美国主要技术委员会及限制规则">
<template #default>
<div class="content-list-item-box content-list-item-box-2">
<div
class="content-list-item content-card-title-text-2 content-card-title-text-4"
v-for="item in ruleList"
:key="item.title"
>
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="8">
<custom-container title="主要限制条款">
<template #default>
<div class="content-list-item-box content-list-item-box-2">
<div
class="content-list-item content-card-title-text-2 content-card-title-text-5"
v-for="item in limitList"
:key="item.href"
>
<el-link type="primary" underline="always">{{ item.href }}</el-link>
<el-button class="content-list-item-button" v-if="item.link" type="primary">官方链接</el-button>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</template>
<script setup>
import { ref } from "vue";
import CustomContainer from "@/components/Container/index.vue";
import BISlogo from "@/assets/images/BISlogo-FCC.png";
import Distribution from "@/assets/images/distribution.png";
// 限制条款
const ruleList = ref([
{
title: '禁止"受限实体"控制的实验室参与认证',
desc: `FCC将不再认可由"受限实体"拥有、控制或指导的测试实验室、认证机构和实验室认可机构。
"受限实体"包括FCC的"受限清单"(Covered List)上的公司,以及美国国防部的"中国军事企业清单"与商务部的"实体清单"上的企业。`
},
{
title: "所有权和控制权的界定",
desc: `任何"受限实体"直接或间接拥有10%或以上股权或投票权的实验室,将被视为"受控"实验室。
实验室必须在30天内向FCC提交声明,证明其不受"受限实体"控制,并报告任何持有5%或以上股权的实体。`
},
{
title: "撤销现有认可",
desc: `FCC将撤销任何被发现由"受限实体"控制的实验室的现有认可。
未能提供准确信息或提供虚假信息的实验室也将失去认可资格。`
},
{
title: "实验室可信度审查",
desc: `覆盖范围:75%的进口电子产品(如IoT设备、智能手机、无线摄像头、智能穿戴等)。
关键标准:实验室需通过FCC"可信度审查",排除与中国军民融合体系、实体清单(Entity List)或国防部"中国军工企业清单"的关联:。`
}
]);
// 主要限制条款
const limitList = ref([
{
href: "FCC Amendments to Equipment Authorization Program",
link: true
},
{
href: "Federal Register Document (2023-02-06)",
link: false
},
{
href: " FCC Covered List",
link: true
}
]);
// 数据对比
const tableData = ref([
{
region: "中国",
num: "168家",
proportion: 10
},
{
region: "美国",
num: "111家",
proportion: 10
},
{
region: "中国台湾",
num: "114家",
proportion: 10
}
]);
</script>
<style scoped>
.content-card {
display: flex;
align-items: flex-start;
gap: 20px;
}
.content-vertical-card {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.renwu-card {
display: flex;
align-items: center;
gap: 20px;
width: 379px;
height: 148px;
background-color: rgba(234, 236, 238, 1);
padding-left: 10px;
}
.rule-time-line-item {
font-size: 16px;
font-weight: 400;
height: 20px;
padding-top: 5px;
padding-bottom: 5px;
color: rgba(95, 101, 108, 1);
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-img {
width: 154px;
height: 154px;
flex-shrink: 0;
}
.content-img-2 {
width: 128px;
height: 128px;
flex-shrink: 0;
}
.content-distribution-img {
width: 627px;
height: 536px;
}
.content-desc {
display: flex;
flex-direction: column;
gap: 8px;
}
.content-desc-item {
display: flex;
gap: 4px;
}
.content-desc-title {
width: 100px;
flex-shrink: 0;
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
}
.content-desc-content {
color: var(--color-main-active);
font-size: 16px;
font-weight: 700;
}
.content-desc-content-long {
color: rgba(95, 101, 108, 1);
font-weight: 400;
}
.time-line {
display: flex;
gap: 20px;
align-items: flex-start;
padding-left: 10px;
margin-bottom: 20px;
}
.time-line-title {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.time-line-dot {
width: 10px;
height: 10px;
}
.time-line-title-text {
font-size: 14px;
font-weight: 700;
color: rgba(95, 101, 108, 1);
color: var(--base-color);
}
.time-line-desc-title {
font-size: 16px;
font-weight: 400;
color: var(--base-color);
margin-bottom: 5px;
}
.time-line-desc-text {
font-size: 14px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
}
.time-line-dot {
width: 8px;
height: 8px;
background-color: var(--color-main-active);
border-radius: 50%;
}
.example-card {
border: 1px solid #ebeef5;
border-radius: 6px;
padding: 16px;
margin-bottom: 16px;
background-color: #fafafa;
}
.content-card-title {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-title-text {
font-size: 16px;
font-weight: 700;
color: rgba(10, 18, 30, 1);
margin-right: auto;
}
.content-list-item-box {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.content-list-item-box-2 {
display: flex;
flex-wrap: wrap;
gap: 18px;
justify-content: center;
}
.content-card-title-text-2 {
display: flex;
flex-direction: column;
gap: 11px;
padding: 10px;
padding-left: 14px;
padding-right: 14px;
margin-bottom: 10px;
border-radius: 4px;
/* box-shadow: 0 2px 8px rgba(60, 87, 126, 0.2); */
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
}
.content-card-title-text-3 {
max-width: 45%;
gap: 19px;
}
.content-card-title-text-4 {
display: flex;
flex-wrap: wrap;
width: 45%;
gap: 10px;
}
.content-card-title-text-5 {
justify-content: flex-start;
width: 100%;
}
.content-list-item-button {
width: 80px;
height: 25px;
}
.content-card-desc {
font-size: 14px;
color: rgba(132, 136, 142, 1);
}
.content-vertical-card-title-box {
display: flex;
align-items: center;
gap: 8px;
}
.content-vertical-card-title-img {
width: 16px;
height: 13px;
}
.content-vertical-card-title {
font-size: 16px;
font-weight: 700;
color: rgba(95, 101, 108, 1);
}
.content-vertical-card-declaration {
/* width: 100%; */
/* height: 42px; */
line-height: 42px;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 15px;
font-size: 14px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
background-color: rgba(231, 243, 255, 1);
}
.content-vertical-card-desc-text {
padding-left: 10px;
font-size: 16px;
font-weight: 400;
color: rgba(132, 136, 142, 1);
}
.content-vertical-card-desc-emphasize {
line-height: 24px;
font-size: 16px;
font-weight: 700;
color: var(--color-main-active);
}
</style>
<template>
<div class="sanction-container">
<!-- 页面标题区域 -->
<div class="page-header">
<!-- <h1 class="main-title">制裁分析</h1> -->
<div class="bill-info">
<div class="bill-details">
<div class="main-title">FCC新规分析:限制中国实验室参与设备认证</div>
<div class="bill-name-en">全面解析美国联邦通信委员会(FCC)2025年新规对中国检测认证行业的影响</div>
</div>
<div class="date-author">
<div class="date">2025年7月</div>
<div class="author">乔迪·阿灵顿(Jodey Arrington)</div>
</div>
</div>
</div>
<!-- 导航标签区域 -->
<!-- <div class="nav-section">
<div class="tabs">
<div class="tab-item active">制裁概况</div>
<div class="tab-item">深度挖掘</div>
<div class="tab-item">影响分析</div>
</div>
<div class="action-buttons">
<el-button>法案原文</el-button>
<el-button type="primary">分析报告</el-button>
</div>
</div> -->
<!-- 内容区域 -->
<!-- <div class="content-section">
<div class="content-placeholder">
<el-icon><document /></el-icon>
<p>这里是制裁概况的内容区域</p>
<p>请选择上方标签查看不同分析内容</p>
</div>
</div> -->
</div>
</template>
<script setup>
const activeTab = ref("制裁概况");
const setActiveTab = tabName => {
activeTab.value = tabName;
};
</script>
<style scoped>
.sanction-container {
width: 100%;
/* max-width: 1200px; */
background-color: #fff;
/* border-radius: 8px; */
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
box-sizing: border-box;
padding-left: 5%;
padding-right: 5%;
}
.page-header {
padding: 24px;
padding-top: 0;
/* border-bottom: 1px solid #ebeef5; */
}
.main-title {
font-size: 20px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
/* color: var(--base-color); */
margin-bottom: 8px;
}
.bill-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 16px;
}
.bill-details {
flex: 1;
}
.bill-name {
font-size: 16px;
color: #606266;
margin-bottom: 4px;
}
.bill-name-en {
font-size: 14px;
color: #909399;
font-style: italic;
}
.date-author {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.date {
font-size: 14px;
color: #606266;
margin-bottom: 4px;
}
.author {
font-size: 14px;
color: #606266;
}
.nav-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
background-color: #f8fafc;
border-bottom: 1px solid #ebeef5;
}
.tabs {
display: flex;
gap: 0;
}
.tab-item {
padding: 12px 24px;
font-size: 14px;
color: #606266;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #409eff;
border-bottom-color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.tab-item:hover {
color: #409eff;
}
.action-buttons {
display: flex;
gap: 12px;
}
.content-section {
padding: 24px;
min-height: 400px;
}
.content-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
color: #909399;
}
.content-placeholder .el-icon {
font-size: 48px;
margin-bottom: 16px;
color: #dcdfe6;
}
.content-placeholder p {
font-size: 16px;
margin-top: 8px;
}
@media (max-width: 768px) {
.bill-info {
flex-direction: column;
align-items: flex-start;
}
.date-author {
align-items: flex-start;
margin-top: 12px;
}
.nav-section {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.tabs {
width: 100%;
overflow-x: auto;
}
}
</style>
<template>
<div class="page-container">
<Header />
<!-- 导航标签区域 -->
<div class="nav-section">
<div class="tabs">
<div :class="['tab-item', { active: activeTab === '简介概览' }]" @click="setActiveTab('简介概览')">制裁概况</div>
<div :class="['tab-item', { active: activeTab === '深度分析' }]" @click="setActiveTab('深度分析')">深度挖掘</div>
<div :class="['tab-item', { active: activeTab === '影响评估' }]" @click="setActiveTab('影响评估')">影响分析</div>
</div>
<div class="action-buttons">
<el-button>法案原文</el-button>
<el-button type="primary">分析报告</el-button>
</div>
</div>
<!-- 内容区域 -->
<div class="content-section">
<!-- <div class="content-placeholder">
<el-icon><document /></el-icon>
<p>这里是制裁概况的内容区域</p>
<p>请选择上方标签查看不同分析内容</p>
</div> -->
<Survey v-if="activeTab === '简介概览'" />
<!-- <DepthMine v-if="activeTab === '深度挖掘'" />
<ImpactAnalysis v-if="activeTab === '影响分析'" /> -->
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { Search, Plus } from "@element-plus/icons-vue";
import Header from "./header.vue";
import Survey from "./content/survey.vue";
// import DepthMine from "./content/depthMine.vue";
// import ImpactAnalysis from "./content/impactAnalysis.vue";
const activeTab = ref("简介概览");
const setActiveTab = tabName => {
activeTab.value = tabName;
};
</script>
<style scoped>
.page-container {
margin: 0px auto;
background-color: rgba(247, 248, 249, 1);
}
.nav-section {
display: flex;
justify-content: space-between;
align-items: center;
/* padding: 16px 24px; */
padding-left: 6%;
padding-right: 6%;
background-color: #fff;
border-bottom: 1px solid #ebeef5;
}
.tabs {
display: flex;
gap: 0;
}
.tab-item {
padding: 12px 24px;
font-size: 14px;
color: #606266;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s;
}
.tab-item.active {
color: #409eff;
border-bottom-color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.tab-item:hover {
color: #409eff;
}
.action-buttons {
display: flex;
gap: 12px;
}
.content-section {
padding: 24px;
padding-left: 6%;
padding-right: 6%;
min-height: 400px;
}
.content-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 300px;
color: #909399;
}
.content-placeholder .el-icon {
font-size: 48px;
margin-bottom: 16px;
color: #dcdfe6;
}
.content-placeholder p {
font-size: 16px;
margin-top: 8px;
}
@media (max-width: 768px) {
.bill-info {
flex-direction: column;
align-items: flex-start;
}
.date-author {
align-items: flex-start;
margin-top: 12px;
}
.nav-section {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.tabs {
width: 100%;
overflow-x: auto;
}
}
</style>
<template>
<div class="page-container">
<div style="height: 30px; width: 960px; display: flex; justify-content: flex-start">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item>国家科技安全</el-breadcrumb-item>
<el-breadcrumb-item>中美博弈概览</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/' }">规则限制</el-breadcrumb-item>
</el-breadcrumb>
</div>
<el-input v-model="searchKey" style="max-width: 960px" placeholder="搜索政策关键词" class="input-with-select">
<template #append>
<el-button :icon="Search" size="large" type="primary">搜索</el-button>
</template>
</el-input>
<div class="page-data">
<div class="page-data-item" v-for="item in pageData" :key="item.desc">
<div class="page-data-item-num">
{{ item.num }}
</div>
<div class="page-data-item-desc">
{{ item.desc }}
</div>
</div>
</div>
<div class="page-content">
<el-row :gutter="20">
<el-col :span="16">
<custom-container title="美国对华规则限制动态">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-law.png" alt="" />
<span class="custom-container-title">美国对华规则限制动态</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>查看详情 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column prop="date" label="更新日期" width="150" />
<el-table-column prop="org" label="机构/组织" width="150" />
<el-table-column prop="rule" label="规则/措施名称" />
<el-table-column prop="content" show-overflow-tooltip width="180" label="主要内容" />
<el-table-column prop="influence" label="影响领域" />
</el-table>
</template>
</custom-container>
</el-col>
<el-col :span="8">
<custom-container title="风险信号">
<template #header-left>
<div class="custom-container-header-left">
<img class="custom-container-icon" src="@/assets/images/icon-warning.png" alt="" />
<span class="custom-container-title">风险信号</span>
<span class="custom-container-count">5</span>
</div>
</template>
<!-- 顶部右侧自定义内容 -->
<template #header-right>
<div style="display: flex; gap: 12px">
<el-button type="primary" link>更多 ></el-button>
</div>
</template>
<!-- 中间内容自定义 -->
<template #default>
<div class="content-list-card" v-for="item in riskData" :key="item.title">
<el-tag :type="item.danger" size="small">{{ item.importance }}</el-tag>
<div class="content-list">
<div class="content-list-title">{{ item.title }}</div>
<div class="content-list-time">{{ item.date }}</div>
</div>
</div>
<div class="content-list-footer">
<el-button type="primary" size="default" style="width: 100%">
<img class="icon-mark" src="@/assets/images/icon-mark.png" alt="" />
风险处理
</el-button>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<custom-container title="美国成立的排华科技联盟">
<template #default>
<div class="content-list-item content-card-title-text-2" v-for="item in policyBanData" :key="item.title">
<div class="content-card-title-box">
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-title-remmber">{{ item.remember.join("、") }}</div>
</div>
<div class="content-card-desc">{{ item.desc }}</div>
<div class="content-card-tag-box">
<div class="content-card-tag-box-item">
<el-tag :type="tagType[Math.floor(Math.random() * 5)]" v-for="tag in item.tag" :key="tag">{{ tag }}</el-tag>
</div>
<!-- <el-button class="content-card-tag-btn" type="primary" link>查看全文 ></el-button> -->
</div>
</div>
</template>
</custom-container>
</el-col>
<el-col :span="12">
<custom-container title="受影响主要中国科技主体">
<template #default>
<div class="content-list-item-box">
<div
class="content-list-item content-card-title-text-2 content-card-title-text-3"
v-for="item in subjectListData"
:key="item.title"
>
<div class="content-card-title-text">{{ item.title }}</div>
<div class="content-card-desc">{{ item.desc }}</div>
<div class="content-card-tag-box">
<div class="content-card-tag-box-item">
<el-tag :type="tagType[Math.floor(Math.random() * 5)]" v-for="tag in item.tag" :key="tag">{{ tag }}</el-tag>
</div>
<!-- <el-button class="content-card-tag-btn" type="primary" link>查看全文 ></el-button> -->
</div>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<custom-container title="美国主要技术委员会及限制规则">
<template #default>
<div class="content-list-item-box content-list-item-box-2">
<div
class="content-list-item content-card-title-text-2 content-card-title-text-4"
v-for="item in orgAndruleList"
:key="item.title"
>
<div class="content-card-title-text">{{ item.org }}</div>
<div class="content-card-desc">主要对华限制</div>
<div class="content-card-list-item" v-for="(subItem, idx) in item.rules" :key="subItem">
<div class="content-card-list-item-num">{{ idx + 1 }}</div>
<div class="content-card-list--item-desc">{{ subItem }}</div>
</div>
</div>
</div>
</template>
</custom-container>
</el-col>
</el-row>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import { ArrowRight } from "@element-plus/icons-vue";
import CustomContainer from "@/components/Container/index.vue";
const searchKey = ref("");
const pageData = ref([
{
num: 310,
desc: "生效政策禁令"
},
{
num: 14433,
desc: "受限制实体"
},
{
num: 126,
desc: "受限制领域"
},
{
num: 37,
desc: "本月更新"
}
]);
// 规则限制
const tableData = ref([
{
date: " 2024年5月14日",
org: "美国商务部",
rule: "AI芯片出口新规",
content: "进一步限制向中国出口高端AI芯片",
influence: "人工智能"
},
{
date: "2023年8月",
org: "美国财政部",
rule: "对华投资限制令",
content: "限制美企在华半导体、AI、量子技术领域投资",
influence: "认证体系"
},
{
date: "2023年7月",
org: "FCC",
rule: "设备授权新规",
content: " 限制中国测试实验室参与美国设备认证",
influence: "投资审查"
},
{
date: "2023年5月",
org: "美国商务部",
rule: "实体清单更新",
content: "新增36家中国科技公司至实体清单",
influence: "出口管制"
},
{
date: "2023年3月",
org: "美国国会",
rule: "CHIPS法案实施细则",
content: "限制获得补贴的企业在华投资先进半导体",
influence: "半导体"
},
{
date: " 2022年1月",
org: "美国商务部",
rule: "AI芯片出口新规",
content: "进一步限制向中国出口高端AI芯片",
influence: "人工智能"
}
]);
// tag类型
const tagType = ["primary", "success", "info", "warning", "danger"];
// 限制动态数据
const limitData = ref([
{
date: "2025年9月2日",
importance: "紧急",
tag: ["半导体", "出口管制"],
title: "美国撤销多家芯片企业在华VEU授权",
desc: `美国最终用户审查委员会(ERC)宣布撤销英特尔、SK海力士和三星在中国工厂的"经验证最终用户"(VEU)授权,影响其在华芯片生产。新规定计划于10月2日《联邦公报》出版120天后正式生效:cite[3]:cite[4]。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 半导体", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 半导体", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
},
{
date: "2025年9月2日",
importance: "紧急",
tag: [" 报道题", "晶圆制造"],
title: "台积电南京厂VEU资格被撤销",
desc: `台积电已收到美国政府通知,其南京工厂的"经验证最终用户"(VEU)资格将于2025年12月31日被撤销。此后,每一批运往南京工厂的受管制设备均需经过美国商务部逐案审批。`
}
]);
// 风险
const riskType = ref(["特别重大", "重大风险", "一般风险"]);
const riskData = ref([
{
date: "一天前",
importance: "特别重大",
danger: "danger",
title: "美国大而美法案落地,总统签署通过"
},
{
date: "两天前",
importance: "重大风险",
danger: "warning",
title: "美大而美法案7月1日以51:50的票数通过..."
},
{
date: "三天前",
importance: "特别重大",
danger: "danger",
title: "首次提出“限制外国敏感实体获取补贴”"
},
{
date: "四天前",
importance: "一般风险",
danger: "success",
title: "将中国企业海外子公司、合资公司纳入受..."
},
{
date: "五天前",
importance: "一般风险",
danger: "success",
title: "H.R.8333《生物安全法案》将华大基因等..."
}
]);
// 政策禁令限制
const policyBanData = ref([
{
title: "芯片四方联盟 (Chip 4)",
desc: `旨在建立半导体供应链安全机制,减少对中国依赖,协调出口管制政策。`,
tag: ["半导体", "供应链"],
remember: ["美国", "日本", "韩国", "台湾"]
},
{
title: "美欧贸易与技术委员会 (TTC)",
desc: `协调美欧在关键技术、数字政策、供应链安全等领域的立场,应对非市场行为。`,
tag: ["技术标准", "供应链"],
remember: ["美国", "欧盟"]
},
{
title: "Quad 关键技术工作组",
desc: `旨在建立半导体供应链安全机制,减少对中国依赖,协调出口管制政策。`,
tag: ["新兴技术", "供应链"],
remember: ["美国", "日本", "印度", "澳大利亚"]
},
{
title: "AUKUS 安全伙伴关系",
desc: `最初聚焦核潜艇技术,现已扩展至网络能力、人工智能、量子技术等高科技领域。`,
tag: ["国防科技", "量子技术"],
remember: ["美国", "英国", "澳大利亚"]
}
]);
// 政策禁令限制
const subjectListData = ref([
{
title: "华为技术有限公司",
desc: `旨在建立半导体供应链安全机制,减少对中国依赖,协调出口管制政策。`,
tag: ["5G", "半导体"]
},
{
title: "中芯国际 (SMIC)",
desc: `被列入实体清单,限制获取先进芯片制造设备,阻碍10nm及以下工艺发展。`,
tag: ["半导体制造", "芯片"]
},
{
title: "海康威视",
desc: `被列入投资黑名单,限制美资投资,限制获取美国AI芯片和技术。`,
tag: ["人工智能", "面部识别"]
},
{
title: "商汤科技",
desc: `最初聚焦核潜艇技术,现已扩展至网络能力、人工智能、量子技术等高科技领域。`,
tag: ["国防科技", "量子技术"]
},
{
title: "长江存储 (YMTC)",
desc: `被列入实体清单,限制获取半导体设备,阻碍NAND闪存技术发展。`,
tag: ["存储芯片", "半导体"]
},
{
title: "大疆创新 (DJI)",
desc: `被列入实体清单,限制技术获取,美国内政部停飞其无人机舰队。`,
tag: ["无人机", "航空科技"]
}
]);
// 政策禁令限制
const orgAndruleList = ref([
{
org: "联邦通信委员会 (FCC)",
rules: [
"《安全与可信通信网络法》禁止使用华为和中兴设备",
"撤销中国电信在美运营授权",
"限制中国测试实验室参与设备认证",
'将多家中国科技公司列入"威胁国家安全"清单'
]
},
{
org: "商务部工业和安全局 (BIS)",
rules: ["实体清单 - 限制技术出口", "军事最终用户清单 (MEU)", "外国直接产品规则 (FDPR)", "半导体制造设备出口管制"]
},
{
org: "外国投资委员会 (CFIUS)",
rules: ["FIRRMA法案扩大审查范围", "限制中国对美关键技术投资", "强制申报特定交易", "针对中国投资者的特别关注"]
},
{
org: "国务院",
rules: ["对中共党员及其直系亲属的签证限制", "STEM专业中国学生签证限制", "中国军民融合战略相关实体限制", "对华技术外交施压"]
}
]);
//
</script>
<style scoped>
.page-container {
display: flex;
flex-direction: column;
align-items: center;
/* height: 100vh; */
margin: 0px auto;
padding: 0px 120px;
padding-top: 40px;
background-color: rgba(234, 236, 238, 1);
}
.page-data {
display: flex;
justify-content: center;
align-items: center;
margin-top: 20px;
width: 960px;
gap: 80px;
}
.page-data-item {
display: flex;
flex-direction: column;
align-items: center;
}
.page-data-item-num {
font-size: 24px;
font-weight: 600;
color: var(--base-color);
}
.page-data-item-desc {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.page-content {
margin-top: 20px;
width: 100%;
/* padding: 0px 20px; */
}
.custom-container-header-left {
display: flex;
align-items: center;
gap: 8px;
padding-left: 20px;
padding-bottom: 5px;
/* border-bottom: 1px solid rgba(234, 236, 238, 1); */
}
.custom-container-title {
font-size: 16px;
font-weight: 600;
color: var(--base-color);
}
.custom-container-count {
text-align: center;
width: 24px;
height: 16px;
border-radius: 100px;
font-size: 12px;
background-color: rgba(255, 77, 79, 1);
color: #fff;
}
.custom-container-icon {
height: 18px;
width: 18px;
}
.content-card {
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
gap: 8px;
padding: 12px 20px;
background-color: rgba(247, 248, 249, 1);
border-radius: 8px;
margin-bottom: 14px;
}
.content-card-date {
font-size: 14px;
font-weight: 400;
color: var(--color-main-active);
}
.content-card-title {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-title-text {
font-size: 16px;
font-weight: 700;
color: rgba(10, 18, 30, 1);
margin-right: auto;
}
.content-list-item-box {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.content-list-item-box-2 {
display: flex;
flex-wrap: wrap;
gap: 18px;
justify-content: center;
}
.content-card-title-text-2 {
display: flex;
flex-direction: column;
gap: 11px;
padding: 10px;
padding-left: 14px;
padding-right: 14px;
margin-bottom: 10px;
border-radius: 4px;
/* box-shadow: 0 2px 8px rgba(60, 87, 126, 0.2); */
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
}
.content-card-title-text-3 {
max-width: 45%;
gap: 19px;
}
.content-card-title-text-4 {
display: flex;
flex-wrap: wrap;
width: 45%;
gap: 10px;
}
.content-card-list-item {
border-bottom: 1px solid rgba(234, 236, 238, 1);
padding-bottom: 5px;
display: flex;
justify-content: flex-start;
align-items: center;
gap: 10px;
}
.content-card-list-item-num {
width: 24px;
height: 24px;
display: flex;
justify-content: center;
align-items: center;
font-size: 12px;
font-weight: 400;
border-radius: 50%;
background-color: rgba(231, 243, 255, 1);
color: var(--color-main-active);
}
.content-card-title-box {
display: flex;
align-items: center;
gap: 8px;
justify-content: space-between;
}
.content-card-title-remmber {
font-size: 14px;
font-weight: 400;
color: rgba(132, 136, 142, 1);
}
.content-card-tag-box {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-card-tag-box-item {
display: flex;
align-items: center;
gap: 6px;
}
.content-card-tag-btn {
margin-left: autp;
}
.content-card-title-tag {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.content-card-desc {
font-size: 12px;
color: rgba(95, 101, 108, 1);
}
.content-list-card {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.content-list {
height: 40px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-row {
height: 60px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
}
.content-list-title {
width: 80%;
font-size: 16px;
font-weight: 400;
color: rgba(59, 65, 75, 1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.content-list-time {
font-size: 14px;
color: rgba(132, 136, 142, 1);
}
.content-list-footer {
width: 100%;
margin-top: 44px;
}
.icon-mark {
height: 12px;
width: 12px;
margin-right: 5px;
margin-left: 8px;
}
.content-card-footer {
display: flex;
width: 100%;
justify-content: center;
margin-top: 30px;
}
</style>
import * as echarts from 'echarts'
const getMultiLineChart = (dataX, dataY1, dataY2) => {
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
top: '8%',
right: '5%',
bottom: '5%',
left: '5%',
containLabel: true
},
legend: {
data: ['提出法案', '通过法案'],
show: false
},
color: ['#1459bb', '#fa8c16'],
xAxis: [
{
type: 'category',
boundaryGap: false,
data: dataX
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '提出法案',
type: 'line',
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(22, 119, 255, 1)' // 起始颜色
}, {
offset: 1,
color: 'rgba(22, 119, 255, 0)' // 结束颜色
}])
},
emphasis: {
focus: 'series'
},
data: dataY1
},
{
name: '通过法案',
type: 'line',
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(255, 172, 77, 1)' // 起始颜色
}, {
offset: 1,
color: 'rgba(255, 172, 77, 0)' // 结束颜色
}])
},
emphasis: {
focus: 'series'
},
data: dataY2
}
]
}
}
export default getMultiLineChart
\ No newline at end of file
<template>
<div class="fishbone">
<div class="main-line"></div>
<div v-for="(causeGroup, groupIndex) in fishboneData.causes" :key="groupIndex" :class="getBoneClass(groupIndex)">
<div class="left-bone">
<div class="left-bone-item" v-for="(item, index) in getLeftItems(causeGroup.causes)" :key="index">
<!-- <div class="icon">
<img :src="item.picture" alt="" />
</div> -->
<div class="text">{{ item.name }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div class="right-bone-item" v-for="(item, index) in getRightItems(causeGroup.causes)" :key="index">
<div class="line"></div>
<div class="text">{{ item.name }}</div>
<!-- <div class="icon">
<img :src="item.picture" :alt="item.name" />
</div> -->
</div>
</div>
</div>
</div>
</template>
<script setup>
import { getChainFishbone } from "@/api/exportControl";
import { onMounted, ref } from "vue";
const chainId = ref(1);
const fishboneData = ref({
text: "",
causes: []
});
// 根据索引确定鱼骨图位置类名
const getBoneClass = index => {
const positions = ["top-bone", "top-bone1", "top-bone2", "bottom-bone", "bottom-bone1", "bottom-bone2"];
return positions[index] || "top-bone";
};
// 获取左侧显示的项目(前半部分)
const getLeftItems = items => {
const midpoint = Math.ceil(items.length / 2);
return items.slice(0, midpoint);
};
// 获取右侧显示的项目(后半部分)
const getRightItems = items => {
const midpoint = Math.ceil(items.length / 2);
return items.slice(midpoint);
};
onMounted(async () => {
try {
const chainFishboneData = await getChainFishbone(chainId.value);
fishboneData.value = chainFishboneData ?? {
text: "",
causes: []
};
console.log("鱼骨图数据:", fishboneData.value);
} catch (error) {
console.log(error);
}
});
</script>
<style lang="scss" scoped>
/* ... 原有的样式保持不变 ... */
.fishbone {
position: relative;
width: 100%;
height: 100%;
margin-top: 40px;
overflow-x: auto;
.main-line {
position: absolute;
top: 280px;
right: 0;
width: 888px;
height: 3px;
background: rgba(174, 208, 255, 1);
}
}
.top-bone {
position: absolute;
top: 20px;
right: 200px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 4px;
height: 70px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.top-bone1 {
position: absolute;
top: 20px;
right: 500px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.top-bone2 {
position: absolute;
top: 20px;
right: 800px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.bottom-bone {
position: absolute;
top: 280px;
right: 360px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.bottom-bone1 {
position: absolute;
top: 280px;
right: 660px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
.bottom-bone2 {
position: absolute;
top: 280px;
right: 960px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.icon {
margin-top: 7px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
</style>
[
{
"name": "美国",
"count": 0.37960339943342775
},
{
"name": "德国",
"count": 0.20113314447592068
},
{
"name": "日本",
"count": 0.1558073654390935
}
]
\ No newline at end of file
<template>
<div class="fishbone-wrapper">
<div class="fishbone-scroll-container" ref="scrollContainerRef">
<div class="fishbone" ref="fishboneRef" v-if="fishboneData.length > 0">
<div class="main-line" :style="{ width: (fishboneData.length / 2) * 340 - 200 + 'px' }"></div>
<!-- 奇数索引的数据组放在上方 -->
<div
v-for="(causeGroup, groupIndex) in getOddGroups(fishboneData)"
:key="'top-' + groupIndex"
:class="getTopBoneClass(groupIndex)"
:style="{ left: groupIndex * 300 + 400 + 'px' }"
>
<div class="left-bone">
<div
class="left-bone-item"
v-for="(item, index) in getLeftItems(causeGroup.causes)"
:key="'left-' + index"
>
<div class="text">{{ item.name }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div
class="right-bone-item"
v-for="(item, index) in getRightItems(causeGroup.causes)"
:key="'right-' + index"
>
<div class="line"></div>
<div class="text">{{ item.name }}</div>
</div>
</div>
</div>
<!-- 偶数索引的数据组放在下方 -->
<div
v-for="(causeGroup, groupIndex) in getEvenGroups(fishboneData)"
:key="'bottom-' + groupIndex"
:class="getBottomBoneClass(groupIndex)"
:style="{ left: groupIndex * 300 + 200 + 'px' }"
>
<div class="left-bone">
<div
class="left-bone-item"
v-for="(item, index) in getLeftItems(causeGroup.causes)"
:key="'left-bottom-' + index"
>
<div class="text">{{ item.name }}</div>
<div class="line"></div>
</div>
</div>
<div class="right-bone">
<div
class="right-bone-item"
v-for="(item, index) in getRightItems(causeGroup.causes)"
:key="'right-bottom-' + index"
>
<div class="line"></div>
<div class="text">{{ item.name }}</div>
</div>
</div>
</div>
</div>
<div v-else style="display: flex; justify-content: center; align-items: center; height: 200px; width: 100%">
<el-empty description="暂无相关数据" />
</div>
</div>
<!-- 滚动指示器 -->
<!-- <div class="scroll-indicators" v-if="showScrollIndicator">
<div class="scroll-btn left" :class="{ disabled: !canScrollLeft }" @click="scrollLeft">‹</div>
<div class="scroll-btn right" :class="{ disabled: !canScrollRight }" @click="scrollRight">›</div>
</div> -->
</div>
</template>
<script setup>
import { getChainFishbone } from "@/api/exportControl";
import { onMounted, ref, nextTick, watch } from "vue";
// 这儿需要接收父组件传递来的产业链ID
const props = defineProps({
chainId: {
type: Number,
default: 1
}
});
// const chainId = ref(1);
const fishboneData = ref([]);
const scrollContainerRef = ref(null);
const fishboneRef = ref(null);
const showScrollIndicator = ref(false);
const canScrollLeft = ref(false);
const canScrollRight = ref(true);
// 获取奇数索引的数据组(放在上方)
const getOddGroups = data => {
console.log(
"getOddGroups:",
data.filter((_, index) => index % 2 === 1)
);
return data.filter((_, index) => index % 2 === 1);
};
// 获取偶数索引的数据组(放在下方)
const getEvenGroups = data => {
console.log(
"getEvenGroups:",
data.filter((_, index) => index % 2 === 0)
);
return data.filter((_, index) => index % 2 === 0);
};
// 获取上方鱼骨图位置类名
const getTopBoneClass = index => {
const positions = ["top-bone", "top-bone1", "top-bone2"];
return positions[index % 3] || "top-bone";
};
// 获取下方鱼骨图位置类名
const getBottomBoneClass = index => {
const positions = ["bottom-bone", "bottom-bone1", "bottom-bone2"];
return positions[index % 3] || "bottom-bone";
};
// 获取左侧显示的项目(前半部分)
const getLeftItems = items => {
const midpoint = Math.ceil(items.length / 2);
return items.slice(0, midpoint);
};
// 获取右侧显示的项目(后半部分)
const getRightItems = items => {
const midpoint = Math.ceil(items.length / 2);
return items.slice(midpoint);
};
// 检查滚动状态
const updateScrollState = () => {
if (!scrollContainerRef.value) return;
const container = scrollContainerRef.value;
canScrollLeft.value = container.scrollLeft > 0;
canScrollRight.value = container.scrollLeft < container.scrollWidth - container.clientWidth;
};
// 滚动处理
const scrollLeft = () => {
if (scrollContainerRef.value) {
scrollContainerRef.value.scrollBy({ left: -200, behavior: "smooth" });
}
};
const scrollRight = () => {
if (scrollContainerRef.value) {
scrollContainerRef.value.scrollBy({ left: 200, behavior: "smooth" });
}
};
// 处理滚动事件
const handleScroll = () => {
updateScrollState();
};
onMounted(async () => {
try {
const chainFishboneData = await getChainFishbone(props.chainId);
fishboneData.value = chainFishboneData?.causes ?? [];
// 等待DOM更新后检查是否需要滚动
nextTick(() => {
if (scrollContainerRef.value && fishboneRef.value) {
showScrollIndicator.value = fishboneRef.value.scrollWidth > scrollContainerRef.value.clientWidth;
updateScrollState();
}
});
console.log("鱼骨图数据:", fishboneData.value);
} catch (error) {
console.log(error);
}
});
// 监听props中的chainId变化
watch(
() => props.chainId,
async () => {
try {
const chainFishboneData = await getChainFishbone(props.chainId);
fishboneData.value = chainFishboneData?.causes ?? [];
} catch (error) {
console.log(error);
}
}
);
</script>
<style lang="scss" scoped>
.fishbone-wrapper {
position: relative;
width: 100%;
height: 100%;
}
.fishbone-scroll-container {
display: flex;
align-items: center;
width: 100%;
height: 100%;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
scrollbar-color: rgba(144, 202, 249, 0.5) transparent;
&::-webkit-scrollbar {
height: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: rgba(144, 202, 249, 0.5);
border-radius: 3px;
}
}
/* ... 原有的样式保持不变 ... */
.fishbone {
position: relative;
width: fit-content;
height: 100%;
margin-top: 40px;
min-width: 100%;
padding-left: 275px;
.main-line {
margin-top: 280px;
width: 1888px;
height: 3px;
background: rgba(174, 208, 255, 1);
}
}
.top-bone {
position: absolute;
top: 20px;
right: 200px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
margin-left: 4px;
height: 70px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.top-bone1 {
position: absolute;
top: 20px;
right: 500px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.top-bone2 {
position: absolute;
top: 20px;
right: 800px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(-30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.bottom-bone {
position: absolute;
top: 280px;
right: 360px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.bottom-bone1 {
position: absolute;
top: 280px;
right: 660px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.bottom-bone2 {
position: absolute;
top: 280px;
right: 960px;
width: 3px;
height: 260px;
background: #90caf9;
transform: skew(-30deg);
z-index: 1;
.left-bone {
color: #777;
position: absolute;
top: 0;
left: -150px;
width: 150px;
height: 260px;
.left-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 5px;
margin-top: 15px;
display: flex;
justify-content: flex-end;
.text {
width: 100px;
margin-left: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.line {
margin-left: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
}
}
.right-bone {
color: #777;
position: absolute;
top: 0;
right: -150px;
width: 150px;
height: 260px;
.right-bone-item {
transform: skew(30deg);
height: 35px;
margin-bottom: 15px;
margin-top: 5px;
display: flex;
justify-content: flex-start;
.line {
margin-right: 7px;
margin-top: 16px;
width: 30px;
height: 2px;
background: rgba(174, 208, 255, 1);
}
.text {
width: 100px;
margin-right: 4px;
height: 35px;
line-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.scroll-indicators {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
pointer-events: none;
padding: 0 10px;
z-index: 10;
}
.scroll-btn {
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.8);
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
color: #90caf9;
cursor: pointer;
pointer-events: auto;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
&:hover:not(.disabled) {
background: #90caf9;
color: white;
transform: scale(1.1);
}
&.disabled {
color: #c0c4cc;
cursor: not-allowed;
background: rgba(255, 255, 255, 0.5);
}
}
</style>
<template>
<div class="hintWrap">
<div class="icon1"></div>
<div class="title">{{text}}</div>
<div class="icon2Wrap">
<div class="icon2"></div>
</div>
</div>
</template>
<script setup>
defineProps({
text: {
type: String,
required: true
}
});
</script>
<style lang="scss" scoped>
.hintWrap {
display: flex;
align-items: center;
padding: 7px 12px;
border: 1px solid rgb(231, 241, 255);
border-radius: 4px;
background: rgb(246, 251, 255);
.icon1 {
width: 19px;
height: 20px;
background-image: url("../../assets//images/aiIcon.png");
background-size: 100% 100%;
}
.title {
color: var(--color-main-active);
font-size: 14px;
font-weight: 400;
line-height: 24px;
margin-left: 20px;
flex: 1;
}
.icon2Wrap {
width: 24px;
height: 24px;
background-color: rgba(231, 243, 255, 1);
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
margin-left: 20px;
.icon2 {
width: 12px;
height: 12px;
background-image: url("../../assets//images/rightIcon.png");
background-size: 100% 100%;
}
}
}
</style>
<template>
<div class="influencePanel1">
<CardCustom title="新增科研机构列表" :style="{ width: '420px', height: '860px' }">
<template #right>
<el-select :style="{ width: '120px', marginRight: '8px' }" size="mini" v-model="selectedId" placeholder="请选择">
<el-option v-for="item in selectOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</template>
<div class="tableWrap">
<el-table
:data="panel5Data"
class="sanction-table"
stripe
empty-text="暂无数据"
header-row-class-name="table-header"
row-class-name="table-row"
>
<el-table-column prop="index" label="序号" width="60">
<template #default="{ $index }">
<div class="index">{{ $index + 1 }}</div>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" min-width="230">
<template #default="{ row }">
<div style="font-weight: 500" class="name">
<img :src="row.img" alt="" class="img" />
{{ row.name }}
</div>
</template>
</el-table-column>
<el-table-column prop="number" label="仪器进口数" width="110"></el-table-column>
</el-table>
</div>
</CardCustom>
<div class="right">
<div class="row">
<CardCustom title="各类别仪器对美依赖情况" :style="{ width: '642px', height: '422px' }">
<div class="subPanel1">
<div class="chartsWrap">
<Echarts :option="horizontalBarOptions" height="100%"></Echarts>
</div>
<Hint
text="受制裁实体中,医学科研仪器对美依赖程度最高,达到50%。此外,电子策略仪器、物理性能测试仪器、激光器、核仪器、计量仪器、分析仪器等也有较高的对美依赖度,本次制裁可能会对相关企业产生较大的影响。"
></Hint>
</div>
</CardCustom>
<CardCustom title="仪器进口国可替代性分析" :style="{ width: '642px', height: '422px' }">
<div class="subPanel2">
<div class="chartsWrap">
<Echarts :option="pieOptions2" height="100%"></Echarts>
</div>
<Hint
text="受制裁实体的仪器来源国主要为美国,但是德国、日本、英国等国也占有较大比重,后续可考虑将上述国家作为仪器进口来源替代国。"
></Hint>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="仪器对美依赖度升高风险分析" :style="{ width: '642px', height: '422px' }">
<div class="subPanel3">
<div class="chartsWrap">
<Echarts :option="multipleLineOptions1" height="100%"></Echarts>
</div>
<Hint
text="近年来,受制裁实体仪器的对美依赖度整体上有所降低,但工艺试验仪器和核仪器的对美依赖程度有抬头趋势,需要加以注意。"
></Hint>
</div>
</CardCustom>
<CardCustom title="仪器国产化降低风险分析" :style="{ width: '642px', height: '422px' }">
<div class="subPanel4">
<div class="chartsWrap">
<Echarts :option="multipleLineOptions2" height="100%"></Echarts>
</div>
<Hint
text="近些年,受制裁实体的国产仪器数量整体上呈增加趋势,但是2023年,各类国产仪器的新增数量均有显著降低,需要加以关注。"
></Hint>
</div>
</CardCustom>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, shallowReactive, shallowRef } from "vue";
import CardCustom from "../../components/CardCustom.vue";
import Echarts from "@/components/Chart/index.vue";
import Hint from "./hint.vue";
import college1 from "../../assets/images/college1.png";
import college2 from "../../assets/images/college2.png";
import college3 from "../../assets/images/college3.png";
import college4 from "../../assets/images/college4.png";
import college5 from "../../assets/images/college5.png";
import college6 from "../../assets/images/college6.png";
import college7 from "../../assets/images/college7.png";
import college8 from "../../assets/images/college8.png";
import college9 from "../../assets/images/college9.png";
import college10 from "../../assets/images/college10.png";
import college11 from "../../assets/images/college11.png";
import { getHorizontalBarChart1, getPieOption2, getMultipleLineChart } from "../../utils/charts";
import {
getScientificImpactEntityList,
getScientificInstrumentDependencyCount,
getScientificInstrumentDependencyRisk,
getScientificInstrumentLocalizationRisk,
getScientificInstrumentOriginCount
} from "@/api/exportControl";
const selectOptions = [
{
value: 1,
label: "对外依赖"
}
];
const selectedId = ref(selectOptions[0].value);
const indexMethod = index => {
return index + 1;
};
const panel5Data = ref([]);
const panel5MockData = [
{
name: "科大讯飞股份有限公司",
number: "1556",
img: college1
},
{
name: "中国科学院物理研究所",
number: "1245",
img: college2
},
{
name: "清华大学",
number: "1032",
img: college3
},
{
name: "复旦大学",
number: "965",
img: college4
},
{
name: "中国水产科学研究院",
number: "910",
img: college5
},
{
name: "上海科技大学",
number: "898",
img: college6
},
{
name: "合肥物质科学研究院",
number: "823",
img: college7
},
{
name: "空天信息创新研究院",
number: "804",
img: college8
},
{
name: "北京航空航天大学",
number: "761",
img: college9
},
{
name: "苏州大学",
number: "752",
img: college10
},
{
name: "电子科技大学",
number: "703",
img: college11
}
];
const horizontalBarOptions = shallowRef({});
const pieOptions2 = shallowRef({});
const multipleLineOptions1 = shallowRef({});
const multipleLineOptions2 = shallowRef({});
// 获取仪器对美依赖度升高风险分析数据
const fetchDependencyRiskData = async () => {
try {
const data = await getScientificInstrumentDependencyRisk();
if (data && data.instrumentCountList && Array.isArray(data.instrumentCountList)) {
// 提取年份数据(去重并排序)
const allYears = [...new Set(data.instrumentCountList.flatMap(item => item.countList.map(c => c.year)))].sort();
// 转换数据格式以适应 getMultipleLineChart
const chartData = data.instrumentCountList.map(item => {
// 为每个仪器类型创建完整的年份数据序列
const values = allYears.map(year => {
const yearData = item.countList.find(c => c.year === year);
return yearData ? yearData.count : 0;
});
return {
name: item.name,
value: values
};
});
// 使用 getMultipleLineChart 生成图表配置
multipleLineOptions1.value = getMultipleLineChart({
dates: allYears.map(String), // 转换为字符串以匹配原数据格式
data: chartData
});
}
} catch (error) {
console.error("获取仪器对美依赖度升高风险分析数据失败:", error);
}
};
// 获取仪器国产化降低风险分析数据
const fetchLocalizationRiskData = async () => {
try {
const data = await getScientificInstrumentLocalizationRisk();
if (data && data.instrumentCountList && Array.isArray(data.instrumentCountList)) {
// 提取年份数据(去重并排序)
const allYears = [...new Set(data.instrumentCountList.flatMap(item => item.countList.map(c => c.year)))].sort();
// 转换数据格式以适应 getMultipleLineChart
const chartData = data.instrumentCountList.map(item => {
// 为每个仪器类型创建完整的年份数据序列
const values = allYears.map(year => {
const yearData = item.countList.find(c => c.year === year);
return yearData ? yearData.count : 0;
});
return {
name: item.name,
value: values
};
});
// 使用 getMultipleLineChart 生成图表配置
multipleLineOptions2.value = getMultipleLineChart({
dates: allYears.map(String), // 转换为字符串以匹配原数据格式
data: chartData
});
}
} catch (error) {
console.error("获取仪器国产化降低风险分析数据失败:", error);
}
};
// 获取各类别仪器对美依赖情况数据
const fetchDependencyCountData = async () => {
try {
const data = await getScientificInstrumentDependencyCount();
if (data && Array.isArray(data)) {
// 按照 count 降序排列
const sortedData = data.sort((a, b) => a.count - b.count);
// 提取仪器名称和对应的计数
const names = sortedData.map(item => item.name);
const counts = sortedData.map(item => item.count);
// 使用 getHorizontalBarChart1 生成图表配置
horizontalBarOptions.value = getHorizontalBarChart1(names, counts, false);
}
} catch (error) {
console.error("获取各类别仪器对美依赖情况数据失败:", error);
}
};
// 获取仪器进口国可替代性分析数据
const fetchOriginCountData = async () => {
try {
const data = await getScientificInstrumentOriginCount();
if (data && Array.isArray(data)) {
// 转换数据格式以适应 getPieOption2
// 数据结构应该是 [{ name: "国家名", value: 数量 }]
const pieData = data.map(item => ({
name: item.name,
value: (item.count * 100).toFixed(1)
}));
// 使用 getPieOption2 生成图表配置
pieOptions2.value = getPieOption2(pieData);
}
} catch (error) {
console.error("获取仪器进口国可替代性分析数据失败:", error);
}
};
const fetchScientificImpactEntityList = async () => {
try {
const data = await getScientificImpactEntityList(router.query.startTime);
if (data && Array.isArray(data)) {
panel5Data.value = data.map(item => ({
name: item.name,
number: item.count
}));
}
} catch (error) {
console.error("获取影响实体分析数据失败:", error);
}
};
onMounted(async () => {
// horizontalBarOptions.value = getHorizontalBarChart1(
// ["地球探测仪器", "计算机及其配套设备", "天文仪器", "分析仪器", "核仪器", "物理性能测试仪器", "医学科研仪器"],
// [10, 10, 21, 25, 79, 95, 109],
// false
// );
// pieOptions2.value = getPieOption2([
// { name: "美国", value: 27 },
// { name: "日本", value: 22 },
// { name: "德国", value: 18 },
// { name: "英国", value: 15 },
// { name: "韩国", value: 12 },
// { name: "荷兰", value: 8 },
// { name: "其他", value: 7 }
// ]);
// multipleLineOptions1.value = getMultipleLineChart({
// dates: ["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
// data: [
// { name: "电子测量仪器", value: [45, 35, 25, 20, 20, 14, 15, 15, 23, 21] },
// { name: "分析仪器", value: [35, 33, 24, 21, 22, 18, 13, 19, 21, 31] },
// { name: "工艺试验仪器", value: [32, 22, 12, 11, 14, 15, 17, 13, 12, 26] },
// { name: "核仪器", value: [48, 38, 28, 28, 28, 18, 18, 18, 28, 28] }
// ]
// });
// multipleLineOptions2.value = getMultipleLineChart({
// dates: ["2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
// data: [
// { name: "电子测量仪器", value: [45, 35, 25, 20, 20, 14, 15, 15, 23, 21] },
// { name: "分析仪器", value: [35, 33, 24, 21, 22, 18, 13, 19, 21, 31] },
// { name: "工艺试验仪器", value: [32, 22, 12, 11, 14, 15, 17, 13, 12, 26] },
// { name: "核仪器", value: [48, 38, 28, 28, 28, 18, 18, 18, 28, 28] }
// ]
// });
// 科研机构
await fetchScientificImpactEntityList();
// 获取各类别仪器对美依赖情况数据
await fetchDependencyCountData();
// 获取仪器进口国可替代性分析数据
await fetchOriginCountData();
// 获取仪器对美依赖度升高风险分析数据
await fetchDependencyRiskData();
// 获取仪器国产化降低风险分析数据
await fetchLocalizationRiskData();
});
</script>
<style lang="scss" scoped>
.influencePanel1 {
display: flex;
gap: 16px;
.tableWrap {
:deep(.table-header) {
color: rgba(59, 65, 75, 1);
font-size: 16px;
font-weight: 700;
height: 64px;
}
:deep(.table-row) {
height: 60px;
font-size: 16px;
font-weight: 400;
}
.index {
width: 24px;
height: 24px;
background-color: rgba(231, 243, 255, 1);
color: rgb(5, 95, 194);
font-size: 12px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 12px;
margin: 0 auto;
}
.name {
display: flex;
align-items: center;
.img {
width: 40px;
height: 40px;
margin-right: 8px;
}
}
}
.right {
display: flex;
flex-direction: column;
gap: 16px;
}
.row {
display: flex;
gap: 16px;
}
}
.subPanel1,
.subPanel2,
.subPanel3,
.subPanel4 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 18px 12px 18px;
.chartsWrap {
flex: 1;
}
}
</style>
<template>
<div class="influencePanel2">
<CardCustom title="涉及行业" :style="{ width: '500px', height: '860px' }">
<div class="subPanel1">
<div class="chartsWrap">
<Echarts :option="horizontalBarOptions" height="100%"></Echarts>
</div>
<div class="buttonWrap">
<ButtonList :list="buttonList" :active-id="activeButtonId" @click="setActiveButtonId"></ButtonList>
</div>
<div class="listWrap">
<div class="item" v-for="(item, index) in listData" :key="index" @click="handleEttClick(item)">
<div class="index" :class="{ isTopTen: index < 10 }">{{ index + 1 }}</div>
<div class="name">{{ item.name || item.nameEn }}</div>
<div class="icon" :class="{ iconUp: item.marketChange > 0, iconDown: item.marketChange < 0 }"></div>
</div>
</div>
</div>
</CardCustom>
<CardCustom title="产业链分析" :style="{ width: '1240px', height: '860px' }">
<div class="subPanel2">
<Hint
text="法案核心意图在于通过税收优惠吸引制造业回流美国​,并在关键科技领域对中国进行遏制,限制中国获取先进技术、资本和市场渠道,从而延缓中国科技产业的发展速度。给半导体、新能源、人工智能等相关科技行业带来不小的短期压力。"
></Hint>
<div class="subPanel2-buttonlist" v-if="chainData.length > 0">
<ButtonList :list="chainData" :active-id="activeChainId" @click="setActiveChainId"></ButtonList>
</div>
<div class="chartsWrap">
<div class="right-main-content">
<div class="right-main-content-main">
<Fishbone :chainId="chainData.length > 0 ? activeChainId : activeButtonId" />
</div>
<div class="right-main-content-footer">
<div class="footer-item1">
<div class="footer-item1-bottom">
<div class="icon">
<img src="../../assets/images/warning.png" alt="" />
</div>
<div class="text">
{{
`国内企业数据量${cnEntityOnChainData.upstreamInternalCount}家(${cnEntityOnChainData.upstreamInternalRate}%),受制裁${cnEntityOnChainData.upstreamEntityCount}家(${cnEntityOnChainData.upstreamEntityRate}%)`
}}
<!-- {{ "中国企业45家(51.00%),受制裁3家(7.00%)" }} -->
</div>
</div>
<div class="footer-item1-top">{{ "上游" }}</div>
</div>
<div class="footer-item2">
<div class="footer-item2-bottom">
<div class="icon">
<img src="../../assets/images/warning.png" alt="" />
</div>
<div class="text">
{{
`国内企业数据量${cnEntityOnChainData.midstreamInternalCount}家(${cnEntityOnChainData.midstreamInternalRate}%),受制裁${cnEntityOnChainData.midstreamEntityCount}家(${cnEntityOnChainData.midstreamEntityRate}%)`
}}
<!-- {{ "中国企业45家(51.00%),受制裁3家(7.00%)" }} -->
</div>
</div>
<div class="footer-item2-top">{{ "中游" }}</div>
</div>
<div class="footer-item3">
<div class="footer-item3-bottom">
<div class="icon">
<img src="../../assets/images/warning.png" alt="" />
</div>
<div class="text">
{{
`国内企业数据量${cnEntityOnChainData.downstreamInternalCount}家(${cnEntityOnChainData.downstreamInternalRate}%),受制裁${cnEntityOnChainData.downstreamEntityCount}家(${cnEntityOnChainData.downstreamEntityRate}%)`
}}
<!-- {{ "中国企业45家(51.00%),受制裁3家(7.00%)" }} -->
</div>
</div>
<div class="footer-item3-top">{{ "下游" }}</div>
</div>
</div>
</div>
</div>
</div>
</CardCustom>
</div>
</template>
<script setup>
import { ref, onMounted, shallowReactive, shallowRef, watch } from "vue";
import CardCustom from "../../components/CardCustom.vue";
import Echarts from "@/components/Chart/index.vue";
import Hint from "./hint.vue";
import ButtonList from "@/components/buttonList/buttonList.vue";
import Fishbone from "./fishbone.vue";
import { getHorizontalBarChart2 } from "../../utils/charts";
import { getDomainDistribution, getChainEntities, getChainInfoByDomainId, getCnEntityOnChain } from "@/api/exportControl";
import { useRoute, useRouter } from "vue-router";
import { useGotoCompanyPages } from "@/router/modules/company";
const gotoCompanyPages = useGotoCompanyPages();
const route = useRoute();
const router = useRouter();
const buttonList = ref([]);
const activeButtonId = ref(buttonList.value[0]?.id || 1);
const setActiveButtonId = id => {
activeButtonId.value = id;
};
const listData = ref([
{
name: "宁德时代新能源科技股份有限公司",
isUp: false
},
{
name: "比亚迪股份有限公司",
isUp: false
},
{
name: "隆基绿能科技股份有限公司",
isUp: false
},
{
name: "晶科能源控股有限公司",
isUp: false
},
{
name: "厦门海辰储能科技股份有限公司",
isUp: false
},
{
name: "国轩高科股份有限公司",
isUp: true
},
{
name: "远景科技集团",
isUp: false
},
{
name: "惠州亿纬锂能股份有限公司",
isUp: false
},
{
name: "天合光能股份有限公司",
isUp: false
},
{
name: "晶澳太阳能科技股份有限公司",
isUp: true
},
{
name: "通威股份有限公司",
isUp: true
},
{
name: "北京海博思创科技股份有限公司",
isUp: true
},
{
name: "上海派能能源科技股份有限公司",
isUp: false
}
]);
const horizontalBarOptions = shallowRef({});
const handleEttClick = item => {
// const route = router.resolve({
// name: "companyPages",
// params: {
// id: item.id
// }
// });
// window.open(route.href, "_blank");
gotoCompanyPages(item.id);
};
// 处理点击事件
const handleChainClick = async chainId => {};
// 获取产业链信息数据
const chainData = ref([]);
const activeChainId = ref(chainData.value[0]?.id);
const setActiveChainId = id => {
activeChainId.value = id;
};
const fetchChainInfo = async () => {
try {
const data = await getChainInfoByDomainId(activeButtonId.value);
console.log("getChainInfoByDomainId", data);
if (data && Array.isArray(data)) {
// 将获取到的数据赋值给 chainData
chainData.value = data.map(item => ({
...item,
text: item.name
}));
console.log("chainData.value", chainData.value);
setActiveChainId(chainData.value[0].id);
} else {
setActiveChainId(null);
chainData.value = [];
}
} catch (error) {
console.error("获取产业链信息数据失败:", error);
}
};
const cnEntityOnChainData = ref({
upstreamInternalCount: 0,
upstreamInternalRate: 0,
upstreamEntityCount: 0,
upstreamEntityRate: 0,
midstreamInternalCount: 0,
midstreamInternalRate: 0,
midstreamEntityCount: 0,
midstreamEntityRate: 0,
downstreamInternalCount: 0,
downstreamInternalRate: 0,
downstreamEntityCount: 0,
downstreamEntityRate: 0
});
const fetchCnEntityOnChain = async () => {
try {
console.log("getCnEntityOnChain 111111", chainData.value);
const data = await getCnEntityOnChain(chainData.value.length > 0 ? activeChainId.value : activeButtonId.value);
console.log("getCnEntityOnChain", data);
if (!!data && Object.keys(data).length > 0) {
// 将获取到的数据赋值给 cnEntityOnChainData
cnEntityOnChainData.value = {
upstreamInternalCount: data.upstreamInternalCount || 0,
upstreamInternalRate: Math.floor(data.upstreamInternalRate * 100) || 0,
upstreamEntityCount: data.upstreamEntityCount || 0,
upstreamEntityRate: Math.floor(data.upstreamEntityRate * 100) || 0,
midstreamInternalCount: data.midstreamInternalCount || 0,
midstreamInternalRate: Math.floor(data.midstreamInternalRate * 100) || 0,
midstreamEntityCount: data.midstreamEntityCount || 0,
midstreamEntityRate: Math.floor(data.midstreamEntityRate * 100) || 0,
downstreamInternalCount: data.downstreamInternalCount || 0,
downstreamInternalRate: Math.floor(data.downstreamInternalRate * 100) || 0,
downstreamEntityCount: data.downstreamEntityCount || 0,
downstreamEntityRate: Math.floor(data.downstreamEntityRate * 100) || 0
};
console.log("getCnEntityOnChain 222222", chainData.value);
}
} catch (error) {
console.error("获取产业链信息数据失败:", error);
}
};
// 获取领域分布数据并更新图表
const fetchDomainDistribution = async () => {
try {
const data = await getDomainDistribution(route.query.startTime);
if (data && Array.isArray(data)) {
console.log("data data11", data);
// 按照 count 降序排列
const sortedData = data.sort((a, b) => a.count - b.count);
// 提取 y 轴数据(领域名称)
const yAxisData = sortedData.map(item => item.name);
// 提取 x 轴数据(数量)
const seriesData = sortedData.map(item => item.count);
// 更新图表配置
horizontalBarOptions.value = getHorizontalBarChart2(yAxisData, seriesData, false);
// 更新buttonList
buttonList.value = sortedData
.map(item => ({
id: item.id,
text: item.name
}))
.sort((a, b) => a.id - b.id);
console.log("buttonList.value", buttonList.value);
setActiveButtonId(buttonList.value[0].id);
}
} catch (error) {
console.error("获取领域分布数据失败:", error);
}
};
const fetchChainEntities = async () => {
try {
const data = await getChainEntities(route.query.startTime, activeButtonId.value);
if (data && Array.isArray(data)) {
console.log("data data", data);
// 更新 listData
listData.value = data.map(item => ({
...item,
name: item.orgNameZh,
nameEn: item.orgName,
isUp: item.isUp
}));
}
} catch (error) {
console.error("获取产业链实体数据失败:", error);
}
};
onMounted(async () => {
fetchDomainDistribution();
fetchChainEntities();
// 获取产业链信息数据
fetchChainInfo();
// 获取产业链中国企业实体信息查询
fetchCnEntityOnChain();
});
watch(
() => activeButtonId.value,
async () => {
await fetchChainEntities();
// 获取产业链信息数据
await fetchChainInfo();
// 获取产业链中国企业实体信息查询
await fetchCnEntityOnChain();
}
);
watch(
() => activeChainId.value,
async () => {
await fetchCnEntityOnChain();
}
);
</script>
<style lang="scss" scoped>
.influencePanel2 {
display: flex;
gap: 16px;
}
.subPanel2 {
height: 100%;
display: flex;
flex-direction: column;
padding: 17px 18px 12px 18px;
&-buttonlist {
padding: 10px;
}
.chartsWrap {
flex: 1;
.right-main-content {
height: 100%;
display: flex;
flex-direction: column;
.right-main-content-main {
flex: 1;
}
.right-main-content-footer {
height: 84px;
margin-left: 14px;
margin-top: 16px;
// background: orange;
display: flex;
.footer-item1 {
width: 408px;
.footer-item1-top {
position: relative;
z-index: 3;
height: 28px;
text-align: center;
line-height: 28px;
background: url("../../assets/images/bg3.png");
background-size: 100% 100%;
color: rgba(22, 119, 255, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 28px;
margin-top: 15px;
}
.footer-item1-bottom {
display: flex;
justify-content: center;
.icon {
margin-top: 9px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-top: 7px;
margin-left: 8px;
height: 22px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.footer-item2 {
margin-left: -8px;
width: 408px;
.footer-item2-top {
position: relative;
z-index: 2;
height: 28px;
text-align: center;
line-height: 28px;
background: url("../../assets/images/bg2.png");
background-size: 100% 100%;
color: rgba(19, 168, 168, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 28px;
margin-top: 15px;
}
.footer-item2-bottom {
display: flex;
justify-content: center;
.icon {
margin-top: 9px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-top: 7px;
margin-left: 8px;
height: 22px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.footer-item3 {
margin-left: -8px;
width: 408px;
.footer-item3-top {
position: relative;
z-index: 1;
height: 28px;
text-align: center;
line-height: 28px;
background: url("../../assets/images/bg1.png");
background-size: 100% 100%;
color: rgba(146, 84, 222, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 28px;
margin-top: 15px;
}
.footer-item3-bottom {
display: flex;
justify-content: center;
.icon {
margin-top: 9px;
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-top: 7px;
margin-left: 8px;
height: 22px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
}
// background: orange;
}
}
}
.subPanel1 {
padding: 0 17px;
height: 100%;
display: flex;
flex-direction: column;
.chartsWrap {
height: 200px;
display: flex;
// flex: 1;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 4px;
background: rgba(247, 248, 249, 1);
}
.buttonWrap {
margin-top: 12px;
margin-bottom: 8px;
}
.listWrap {
flex: 1;
height: 42px;
min-height: 0;
overflow: auto;
display: flex;
flex-direction: column;
cursor: pointer;
.item {
display: flex;
align-items: center;
height: 42px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
overflow: hidden;
flex-shrink: 0;
.index {
width: 24px;
height: 24px;
border-radius: 12px;
color: rgb(5, 95, 194);
font-size: 14px;
font-weight: 400;
display: flex;
justify-content: center;
align-items: center;
margin-left: 12px;
margin-right: 10px;
flex-shrink: 0;
}
.isTopTen {
background-color: rgba(231, 243, 255, 1);
}
.name {
color: rgb(59, 65, 75);
font-size: 16px;
font-weight: 400;
line-height: 24px;
height: 24px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.icon {
width: 8px;
height: 6px;
background-size: 100% 100%;
margin-left: 10px;
flex-shrink: 0;
}
.iconUp {
background-image: url("../../assets/images/iconUp.png");
}
.iconDown {
background-image: url("../../assets/images/iconDown.png");
}
}
}
}
</style>
<template>
<div class="mapChartsWrap">
<Echarts :option="mapOption"></Echarts>
</div>
</template>
<script setup>
import Echarts from "@/components/Chart/index.vue";
import { getMapOption } from "../../utils/charts";
import { ref, onMounted, shallowRef } from "vue";
import { getAreaDistribution } from "@/api/exportControl";
// 这儿接收父组件传递过来的date参数
const props = defineProps({
date: {
type: String,
default: ""
}
});
const mapOption = shallowRef({});
onMounted(() => {
mapOption.value = getMapOption();
// 区域分布查询
getAreaDistribution(props.date).then(res => {
console.log("res", res);
});
});
</script>
<style lang="scss" scoped>
.mapChartsWrap {
width: 100%;
height: 100%;
}
</style>
<template>
<div class="panel1Wrap">
<div class="row">
<CardCustom title="实体清单制裁强度" :style="{ width: '798px', height: '422px' }">
<template #right>
<el-select
:style="{ width: '120px', marginRight: '8px' }"
size="mini"
v-model="domainValue"
placeholder="领域选择"
@change="handleDomainChange"
>
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
<el-select
:style="{ width: '120px', marginRight: '18px' }"
size="mini"
v-model="typeValue"
placeholder="类型选择"
>
<el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</template>
<div class="subPanel1">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts v-if="!bar1DataIsEmpty" :option="bar1Option" height="100%"></Echarts>
<el-empty v-else description="暂无数据" />
</div>
<Hint text="近几次新增受制裁实体中,中国实体占比提高,美方针对中国的出口管制风险显著增加。"></Hint>
</div>
</CardCustom>
<CardCustom title="新增实体数量增长趋势" :style="{ width: '798px', height: '422px' }">
<div class="subPanel2">
<div class="chartsWrap">
<Echarts :option="line1Option" height="100%"></Echarts>
</div>
<Hint
text="近年来美国对中国新增实体数量总体呈上升趋势,2018-2023 年 BIS 针对中国发布 “实体清单”
的频次逐年攀升,拜登政府时期新增实体数量已超越特朗普政府时期。"
></Hint>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="实体清单更新频率" :style="{ width: '798px', height: '422px' }">
<div class="subPanel3">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar2Option" height="100%"></Echarts>
</div>
<Hint text="2024年以来,实体清单更新次数相对于2023年有所降低,但是本年度内呈上升趋势。"></Hint>
</div>
</CardCustom>
<CardCustom title="重点实体列表" :style="{ width: '798px', height: '422px' }">
<template #right>
<el-input v-model="value3" placeholder="搜索" size="mini" :style="{ width: '300px', marginRight: '8px' }">
<template #suffix>
<i class="searchIcon"></i>
</template>
</el-input>
</template>
<div class="subPanel4">
<div class="listWrap">
<div class="item" v-for="(item, index) in subPanel4" :key="index">
<div class="name" @click="handleOrgClick(item)">{{ item.name }}</div>
<div class="tags">
<div
class="tagItem"
v-for="(item, index) in item.tags"
:key="index"
:class="`tagLevel${item.level}`"
>
{{ item.name }}
</div>
</div>
</div>
</div>
<Hint
text="本次实体清单更新,科研院所占比相对较高。需要注意的是,中国科学技术大学、中国科学院物理研究所等国内重点科研机构被列入实体清单,此外还有中科星图以及中电科旗下的多家研究所被列入清单,可能会对相关行业产生显著影响。"
></Hint>
</div>
</CardCustom>
</div>
</div>
</template>
<script setup>
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 Hint from "./hint.vue";
import { getEntitiesChangeCount, getEntitiesGrowthTrend, getEntitiesUpdateCount, getKeyEntityList } from "@/api/exportControl";
import _ from "lodash";
import { useRoute, useRouter } from "vue-router";
import { useGotoCompanyPages } from "@/router/modules/company";
const gotoCompanyPages = useGotoCompanyPages();
const route = useRoute();
const router = useRouter();
const line1Option = shallowRef({});
const bar2Option = shallowRef({});
// 获取重点实体列表数据
const fetchKeyEntityList = async (date = "2025-11-11", keyword = "") => {
try {
const data = await getKeyEntityList(date, keyword);
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例: [{orgName: "...", orgNameZh: "...", ...}]
subPanel4.value = data.map(item => {
// 优先使用中文名称,如果没有则使用英文名称
const name = item.orgNameZh || item.orgName || "未知实体";
return {
...item,
name,
tags: item.domainList || []
};
});
}
} catch (err) {
console.error("获取重点实体列表失败:", err);
}
};
onMounted(async () => {
try {
const [entitiesGrowthTrendData, entitiesUpdateCountData] = await Promise.all([
getEntitiesGrowthTrend(),
getEntitiesUpdateCount(1)
]);
const list = _.reverse(entitiesGrowthTrendData);
const xAxisData = _.map(list, "year");
const seriesData = _.map(list, item => {
return parseInt(item.percent);
});
line1Option.value = getLineChart({ xAxisData, seriesData, name: "增长趋势", color: "rgba(146, 84, 222, 1)" }, true);
if (entitiesUpdateCountData && Array.isArray(entitiesUpdateCountData)) {
const sortedData = _.sortBy(entitiesUpdateCountData, "year");
// 提取 x 轴数据(年份)
const xAxisData = sortedData.map(item => item.year.toString());
// 提取 y 轴数据(数量)
const seriesData = sortedData.map(item => item.count);
bar2Option.value = getBarChart(xAxisData, seriesData, ["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"], "更新频率");
} else {
// 保持原有逻辑以防数据格式不符合预期
bar2Option.value = getBarChart(
_.reverse(entitiesUpdateCountData.xAxis),
_.reverse(entitiesUpdateCountData.series),
["rgba(22, 119, 255, 1)", "rgba(22, 119, 255, 0)"],
"更新频率"
);
}
// 获取重点实体列表数据
await fetchKeyEntityList(route.query.startTime);
} catch (err) {
console.log(err);
}
});
const domainOptions = [
{
value: "all",
label: "全部"
},
{
value: "1",
label: "人工智能"
},
{
value: "2",
label: "生物科技"
},
{
value: "3",
label: "新一代信息技术"
},
{
value: "4",
label: "量子科技"
},
{
value: "5",
label: "新能源"
},
{
value: "6",
label: "集成电路"
},
{
value: "7",
label: "海洋"
},
{
value: "8",
label: "先进制造"
},
{
value: "9",
label: "新材料"
},
{
value: "10",
label: "航空航天"
},
{
value: "11",
label: "深海"
},
{
value: "12",
label: "极地"
},
{
value: "13",
label: "太空"
},
{
value: "14",
label: "核"
},
{
value: "20",
label: "政治"
},
{
value: "21",
label: "外交"
},
{
value: "22",
label: "经济"
},
{
value: "23",
label: "军事"
},
{
value: "24",
label: "科技"
},
{
value: "25",
label: "安全"
},
{
value: "99",
label: "其他"
}
];
const typeOptions = [
{
value: "all",
label: "全部"
},
{
value: "1",
label: "人物"
},
{
value: "2",
label: "机构"
},
{
value: "7",
label: "地址"
}
];
const domainValue = ref("all");
const typeValue = ref("all");
const bar1Option = shallowRef({});
const bar1DataIsEmpty = ref(false);
watch(
[domainValue, typeValue],
async ([domain, type]) => {
let EntitiesChangeCount = await getEntitiesChangeCount(domain == "all" ? "" : domain, type == "all" ? "" : type);
EntitiesChangeCount = _.reverse(EntitiesChangeCount);
bar1DataIsEmpty.value = EntitiesChangeCount.length === 0;
bar1Option.value = getBarChart(
_.map(EntitiesChangeCount, "year"),
_.map(EntitiesChangeCount, "count"),
["rgba(255, 159, 22, 1)", "rgba(255, 159, 22, 0)"],
"制裁强度"
);
},
{ immediate: true }
);
const subPanel4 = ref([
{
name: "中国科学技术大学",
tags: [
{
name: "双一流",
level: 1
},
{
name: "985",
level: 2
},
{
name: "关税政策",
level: 3
}
]
},
{
name: "合肥量子信息科学国家实验室",
tags: [
{
name: "上市企业",
level: 2
}
]
},
{
name: "中国科学院物理研究所",
tags: [
{
name: "科研院所",
level: 4
}
]
}
]);
const value3 = ref("");
// 监听搜索关键词变化,重新获取数据
watch(
value3,
_.debounce(async newVal => {
await fetchKeyEntityList(route.query.startTime, newVal);
}, 300)
);
const handleDomainChange = async domain => {
await fetchKeyEntityList(route.query.startTime, value3.value, domain);
};
const handleOrgClick = item => {
console.log(item);
// const route = router.resolve({
// name: "companyPages",
// params: {
// id: item.id
// }
// });
// window.open(route.href, "_blank");
gotoCompanyPages(item.id);
};
</script>
<style lang="scss" scoped>
.panel1Wrap {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.subPanel1,
.subPanel2,
.subPanel3 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 18px 12px 18px;
.chartsWrap {
flex: 1;
}
}
.subPanel4 {
height: 100%;
display: flex;
flex-direction: column;
padding: 4px 20px 12px 20px;
.listWrap {
flex: 1;
min-height: 0;
overflow: auto;
.item {
height: 44px;
display: flex;
align-items: center;
border-bottom: 1px solid rgba(234, 236, 238, 1);
.name {
flex: 1;
min-width: 0;
color: rgba(10, 18, 30, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-left: 20px;
white-space: nowrap;
overflow: hidden;
margin-right: 11px;
text-overflow: ellipsis;
cursor: pointer;
}
.tags {
display: flex;
align-items: center;
gap: 8px;
.tagItem {
padding: 2px 8px;
font-size: 14px;
font-weight: 400;
line-height: 20px;
border: 1px solid transparent;
border-radius: 4px;
}
.tagLevel1 {
border: 1px solid rgba(255, 163, 158, 1);
background-color: rgba(255, 241, 240, 1);
color: rgba(245, 34, 45, 1);
}
.tagLevel2 {
border: 1px solid rgba(135, 232, 222, 1);
background-color: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
}
.tagLevel3 {
border: 1px solid rgba(255, 229, 143, 1);
background-color: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
}
.tagLevel4 {
border: 1px solid rgba(33, 129, 57, 1);
background-color: rgba(33, 129, 57, 0.1);
color: rgba(33, 129, 57, 1);
}
}
}
}
}
.searchIcon {
width: 16px;
height: 16px;
background-image: url("../../assets/images/search.png");
background-size: 100% 100%;
}
</style>
<template>
<div class="panel2Wrap">
<div class="row">
<CardCustom title="上市企业制裁强度" :style="{ width: '798px', height: '422px' }">
<div class="subPanel1">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar1Option" height="100%"></Echarts>
</div>
<Hint text="这些企业大多集中在半导体领域,美国制裁旨在限制中国先进半导体技术及相关制造能力。"></Hint>
</div>
</CardCustom>
<CardCustom title="上市企业融资变化情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel2">
<div class="chartsWrap">
<Echarts :option="line1Option" height="100%"></Echarts>
</div>
<Hint
text="最近 10 年中国上市企业融资情况呈现出先波动上升,在 2022 年达到峰值后,2023-2024 年又快速下滑的趋势。"
></Hint>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="上市企业市值变化情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel3">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar2Option" height="100%"></Echarts>
</div>
<Hint text="不仅对企业自身发展和市值产生了冲击,也反映了中美在经济、科技领域的博弈不断加剧。"></Hint>
</div>
</CardCustom>
<CardCustom title="重点上市企业列表" :style="{ width: '798px', height: '422px' }">
<template #right>
<el-input
v-model="value3"
placeholder="请输入关键词,使用(、)作为分隔符"
size="mini"
:style="{ width: '300px', marginRight: '8px' }"
>
<template #suffix>
<i class="searchIcon"></i>
</template>
</el-input>
</template>
<div class="subPanel4">
<div class="listWrap">
<div class="item" v-for="(item, index) in subPanel4" :key="index">
<div class="name" @click="handleOrgClick(item)">{{ 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="detail" @click="handleOrgClick(item)">查看详情</div>
</div>
</div>
</div>
<Hint
text="本次实体清单更新,科研院所占比相对较高。需要注意的是,中国科学技术大学、中国科学院物理研究所等国内重点科研机构被列入实体清单,此外还有中科星图以及中电科旗下的多家研究所被列入清单,可能会对相关行业产生显著影响。"
></Hint>
</div>
</CardCustom>
</div>
</div>
</template>
<script setup>
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, getEntityMarketValue, getKeyListedEntityList, getSanStrength } from "@/api/exportControl";
import { useRoute, useRouter } from "vue-router";
import { useGotoCompanyPages } from "@/router/modules/company";
const gotoCompanyPages = useGotoCompanyPages();
const route = useRoute();
const router = useRouter();
const value3 = ref("");
const bar1Option = shallowRef({});
const bar2Option = shallowRef({});
const line1Option = shallowRef({});
const subPanel4 = ref([
{
name: "中科星图有限公司",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "上市企业"
},
{
name: "中国科学院长春光学精密机械与物理研究所",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "研究院"
},
{
name: "中芯国际集成电路制造有限公司",
shizhi: "278.47亿",
address: "北京",
hangye: "半导体",
type: "上市企业"
}
]);
// 获取重点上市企业列表数据
const fetchKeyListedEntityList = async (keyword = "") => {
try {
console.log("获取重点上市企业列表数据", route.query.startTime);
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 {
...item,
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 {
const data = await getEntityFinancing();
if (data && Array.isArray(data)) {
// 根据 fishbone-mock.json 的数据结构处理数据
// 数据结构示例: [{name: "2025-10-09", count: 41}, ...]
// 按日期排序
const sortedData = data
.sort((a, b) => {
return new Date(a.year) - new Date(b.year);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.year);
// 提取 y 轴数据(融资数量)
const seriesData = sortedData.map(item => item.count);
// 更新图表配置
line1Option.value = getLineChart({
xAxisData,
seriesData,
name: "融资变化",
color: "rgba(146, 84, 222, 1)"
});
}
} catch (error) {
console.error("获取上市企业融资变化数据失败:", error);
}
};
// 获取上市企业市值变化数据
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.year) - new Date(b.year);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.year);
// 提取 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.year) - new Date(b.year);
})
.filter((item, idx) => idx % 3 === 0);
// 提取 x 轴数据(日期)
const xAxisData = sortedData.map(item => item.year);
// 提取 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)"],
// "市值变化"
// );
// line1Option.value = getLineChart({
// xAxisData: [2013, 2023, 2024, 2015],
// seriesData: [434, 24, 453, 322],
// name: "融资变化",
// color: "rgba(146, 84, 222, 1)"
// });
// 获取上市企业融资变化数据
fetchEntityFinancing();
fetchEntityMarketValue();
fetchKeyListedEntityList();
fetchSanStrength();
});
// 监听搜索关键词变化,重新获取数据
watch(
value3,
_.debounce(async newVal => {
console.log("关键词变化:", newVal);
await fetchKeyListedEntityList(newVal);
}, 300)
);
const handleOrgClick = item => {
console.log(item);
// const route = router.resolve({
// name: "companyPages",
// params: {
// id: item.id
// }
// });
// window.open(route.href, "_blank");
gotoCompanyPages(item.id);
};
</script>
<style lang="scss" scoped>
.panel2Wrap {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.subPanel1,
.subPanel2,
.subPanel3 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 18px 12px 18px;
.chartsWrap {
flex: 1;
}
}
.subPanel4 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 20px 12px 20px;
.listWrap {
flex: 1;
min-height: 0;
overflow: auto;
display: flex;
flex-direction: column;
gap: 8px;
.item {
display: flex;
flex-direction: column;
border: 1px solid rgba(234, 236, 238, 1);
gap: 10px;
padding: 11px 20px;
flex-shrink: 0;
.name {
color: rgba(10, 18, 30, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
.infoWrap {
display: flex;
color: rgba(132, 136, 142, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
.shizhi {
width: 180px;
flex-shrink: 0;
}
.address {
width: 130px;
flex-shrink: 0;
margin-right: 20px;
}
.hangye {
width: 130px;
flex-shrink: 0;
}
.type {
width: 130px;
flex-shrink: 0;
}
.detail {
margin-left: auto;
flex-shrink: 0;
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
}
}
}
}
.searchIcon {
width: 16px;
height: 16px;
background-image: url("../../assets/images/search.png");
background-size: 100% 100%;
}
</style>
<template>
<div class="panel3Wrap">
<div class="row">
<CardCustom title="新增实体领域分布情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel1">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="pie1Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国的制裁领域以半导体等高精尖领域企业。"></Hint>
</div>
</CardCustom>
<CardCustom title="量子科技领域历史制裁情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel2">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="line1Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国量子科技领域制裁日益增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="历次制裁涉及领域数" :style="{ width: '798px', height: '422px' }">
<div class="subPanel3">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar2Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国制裁涉及领域数逐年增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
<CardCustom title="航空航天领域历史制裁情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel4">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="line2Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国航空领域领域制裁日益增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
</div>
</div>
</template>
<script setup>
import { ref, shallowRef, onMounted } 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, getPieOption1 } from "../../utils/charts";
import Hint from "./hint.vue";
import {
getEntitiesAreaCountByYear,
getEntitiesDomainCount,
getCountThisDomain,
getDomainDistribution
} from "@/api/exportControl";
import _ from "lodash";
import { useRoute } from "vue-router";
const route = useRoute();
const pie1Option = shallowRef({});
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, countThisDomainData10] = await Promise.all([
getDomainDistribution(route.query.startTime),
getCountThisDomain("4"),
// getEntitiesDomainCount(),
getCountThisDomain("10")
]);
pie1Option.value = getPieOption1(
entitiesAreaCountByYearData.map(item => ({
name: item.name,
value: item.count
})) ?? []
);
const list4 = _.reverse(countThisDomainData4 ?? []);
line1Option.value = getLineChart({
xAxisData: _.map(list4, "year"),
seriesData: _.map(list4, "count"),
name: "量子科技领域",
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)"],
// "领域数"
// );
const list10 = _.reverse(countThisDomainData10 ?? []);
line2Option.value = getLineChart({
xAxisData: _.map(list10, "year"),
seriesData: _.map(list10, "count"),
name: "航空航天领域",
color: "rgba(146, 84, 222, 1)"
});
await fetchEntitiesDomainCount();
} catch (err) {
console.log(err);
}
});
</script>
<style lang="scss" scoped>
.panel3Wrap {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.subPanel1,
.subPanel2,
.subPanel3,
.subPanel4 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 18px 12px 18px;
.chartsWrap {
flex: 1;
}
}
</style>
<template>
<div class="panel4Wrap">
<div class="row">
<CardCustom title="新增实体类别分布情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel1">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="pie1Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国的制裁领域以企业、科研院所为主。"></Hint>
</div>
</CardCustom>
<CardCustom title="科研院所类实体历史制裁情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel2">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="line1Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国科研院所制裁逐年增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="历次制裁涉及实体类型数" :style="{ width: '798px', height: '422px' }">
<div class="subPanel3">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="bar2Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国制裁涉及领域数逐年增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
<CardCustom title="企业类实体历史制裁情况" :style="{ width: '798px', height: '422px' }">
<div class="subPanel4">
<div class="chartsWrap" :style="{ paddingBottom: '10px' }">
<Echarts :option="line2Option" height="100%"></Echarts>
</div>
<Hint text="美国对中国企业制裁逐年增加,对华制裁日益严峻。"></Hint>
</div>
</CardCustom>
</div>
</div>
</template>
<script setup>
import { ref, shallowRef, onMounted } 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, getPieOption1 } from "../../utils/charts";
import Hint from "./hint.vue";
import { getCountSanTypeByTime, getCountTypeByYear, getCountThisType, getEnterpriseSanCount } from "@/api/exportControl";
import _ from "lodash";
import { useRoute } from "vue-router";
const route = useRoute();
const pie1Option = shallowRef({});
const bar2Option = shallowRef({});
const line1Option = shallowRef({});
const line2Option = shallowRef({});
onMounted(async () => {
try {
const [countSanTypeByTimeData, countThisTypeData1, countTypeByYearData, countThisTypeData2] = await Promise.all([
getCountSanTypeByTime(route.query.startTime),
getCountThisType("1"),
getCountTypeByYear(),
getEnterpriseSanCount()
]);
pie1Option.value = getPieOption1(
_.map(countSanTypeByTimeData ?? [], item => {
return {
name: item?.name,
value: item?.count
};
})
);
const list1 = _.reverse(countThisTypeData1 ?? []);
line1Option.value = getLineChart({
xAxisData: _.map(list1, "year"),
seriesData: _.map(list1, "count"),
name: "科研院所类",
color: "rgba(255, 149, 77, 1)"
});
const list = _.reverse(countTypeByYearData ?? []);
bar2Option.value = getBarChart(
_.map(list, "year"),
_.map(list, "count"),
["rgba(19, 188, 196, 1)", "rgba(19, 188, 196, 0)"],
"实体数"
);
const list2 = _.reverse(countThisTypeData2 ?? []);
line2Option.value = getLineChart({
xAxisData: _.map(list2, "year"),
seriesData: _.map(list2, "count"),
name: "企业类",
color: "rgba(117, 29, 219, 1)"
});
} catch (err) {
console.log(err);
}
});
</script>
<style lang="scss" scoped>
.panel4Wrap {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.subPanel1,
.subPanel2,
.subPanel3,
.subPanel4 {
height: 100%;
display: flex;
flex-direction: column;
padding: 2px 18px 12px 18px;
.chartsWrap {
flex: 1;
}
}
</style>
<template>
<div class="pieChartsWrap">
<div class="item">
<Echarts :option="pie1Option"></Echarts>
</div>
<div class="item">
<Echarts :option="pie2Option"></Echarts>
</div>
</div>
</template>
<script setup>
import Echarts from "@/components/Chart/index.vue";
import { getPieOption } from "../../utils/charts";
import { ref, onMounted, shallowRef } from "vue";
import { getCountByDomain, getCountByType, getDomainDistribution } from "@/api/exportControl";
import _ from "lodash";
import { useRoute } from "vue-router";
const route = useRoute();
const pie1Option = shallowRef({});
const pie2Option = shallowRef({});
onMounted(async () => {
try {
const [countByDomainData, countByTypeData] = await Promise.all([
getDomainDistribution(route.query.startTime),
getCountByType(route.query.startTime)
]);
pie1Option.value = getPieOption(
_.chain(countByDomainData)
.filter(item => item.count > 0)
.map(item => {
return {
name: item?.name,
value: item?.count
};
})
.value(),
"领域分布"
);
pie2Option.value = getPieOption(
_.chain(countByTypeData)
.filter(item => item.count > 0)
.map(item => {
return {
name: item?.name,
value: item?.count
};
})
.value(),
"类型分布"
);
} catch (err) {
console.log(err);
}
});
onMounted(() => {
// pie1Option.value = getPieOption(
// [
// { name: "能源领域", value: 30 },
// { name: "集成电路领域", value: 20 },
// { name: "人工智能领域", value: 12 },
// { name: "通信网络领域", value: 18 },
// { name: "量子科技领域", value: 10 },
// { name: "生物科技领域", value: 10 }
// ],
// "领域分布"
// );
// pie2Option.value = getPieOption(
// [
// { name: "能源领域", value: 30 },
// { name: "集成电路领域", value: 20 },
// { name: "人工智能领域", value: 12 },
// { name: "通信网络领域", value: 18 },
// { name: "量子科技领域", value: 10 },
// { name: "生物科技领域", value: 10 }
// ],
// "类别分布"
// );
});
</script>
<style lang="scss" scoped>
.pieChartsWrap {
width: 100%;
height: 100%;
display: flex;
align-items: flex-start;
justify-content: center;
gap: 16px;
padding-top: 6px;
.item {
width: 460px;
height: 388px;
background-color: rgba(231, 243, 255, 0.5);
}
}
</style>
<template>
<div class="deepDigContainer">
<div class="row">
<div class="panelButtonList">
<div
class="item"
:class="{ activeItem: activePanelId === item.id }"
v-for="item in compareCountSan"
:key="item.id"
@click="setActivePanelId(item.id)"
>
<div
class="line"
:class="{
lineActive: activePanelId === item.id,
lineUp: activePanelId !== item.id && item.isUp,
lineDown: activePanelId !== item.id && !item.isUp
}"
></div>
<div class="infoWrap">
<div
class="number"
:class="{
numberActive: activePanelId === item.id,
numberUp: activePanelId !== item.id && item.isUp,
numberDown: activePanelId !== item.id && !item.isUp
}"
>
{{ item.number }}
</div>
<div class="name">{{ item.name }}</div>
</div>
<div class="text">较上次{{ item.isUp ? "增加" : "减少" }}{{ item.ComparisonNumber }}</div>
<div
class="icon"
:class="{
iconActiveUp: activePanelId === item.id && item.isUp,
iconActiveDown: activePanelId === item.id && !item.isUp,
iconUp: activePanelId !== item.id && item.isUp,
iconDown: activePanelId !== item.id && !item.isUp
}"
></div>
</div>
</div>
</div>
<div class="row">
<panel1 v-if="activePanelId === compareCountSan[0]?.id"></panel1>
<panel2 v-if="activePanelId === compareCountSan[1]?.id"></panel2>
<panel3 v-if="activePanelId === compareCountSan[2]?.id"></panel3>
<panel4 v-if="activePanelId === compareCountSan[3]?.id"></panel4>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, shallowRef } from "vue";
import Panel1 from "../components/panel1.vue";
import Panel2 from "../components/panel2.vue";
import Panel3 from "../components/panel3.vue";
import Panel4 from "../components/panel4.vue";
import { getCompareCountSan } from "@/api/exportControl";
import { useRoute } from "vue-router";
const route = useRoute();
const compareCountSan = shallowRef([]);
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 { entityNum, entityChange, listedCompanyNum, listedCompanyChange, domainNum, domainChange, typeNum, typeChange } =
compareCountSanData ?? {};
compareCountSan.value = [
{
id: 1,
name: "新增实体数量",
number: entityNum,
isUp: entityChange >= 0,
ComparisonNumber: Math.abs(entityChange)
},
{
id: 2,
name: "上市企业数量",
number: listedCompanyNum,
isUp: listedCompanyChange >= 0,
ComparisonNumber: Math.abs(listedCompanyChange)
},
{
id: 3,
name: "涉及领域数量",
number: domainNum,
isUp: domainChange >= 0,
ComparisonNumber: Math.abs(domainChange)
},
{
id: 4,
name: "实体类别数量",
number: typeNum,
isUp: typeChange >= 0,
ComparisonNumber: Math.abs(typeChange)
}
];
activePanelId.value = compareCountSan.value[0].id;
} catch (err) {
console.log(err);
}
});
const setActivePanelId = id => {
activePanelId.value = id;
};
</script>
<style lang="scss" scoped>
.deepDigContainer {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.panelButtonList {
display: flex;
justify-content: center;
gap: 16px;
.item {
width: 388px;
height: 80px;
border-radius: 4px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
cursor: pointer;
.line {
width: 4px;
height: 49px;
margin-right: 26px;
}
.lineActive {
background-color: rgba(255, 255, 255, 1);
}
.lineUp {
background-color: rgba(206, 79, 81, 1);
}
.lineDown {
background-color: rgba(33, 129, 57, 1);
}
.infoWrap {
flex: 1;
display: flex;
flex-direction: column;
.number {
font-size: 30px;
font-weight: 700;
line-height: 24px;
}
.numberActive {
color: rgba(255, 255, 255, 1);
}
.numberUp {
color: rgba(206, 79, 81, 1);
}
.numberDown {
color: rgba(33, 129, 57, 1);
}
.name {
color: rgba(34, 41, 52, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
.text {
color: rgba(34, 41, 52, 1);
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.icon {
width: 32px;
height: 32px;
margin-right: 15px;
margin-left: 8px;
background-size: 100% 100%;
}
.iconActiveUp {
background-image: url("../../assets/images/activeUp.png");
}
.iconActiveDown {
background-image: url("../../assets/images/activeDown.png");
}
.iconUp {
background-image: url("../../assets/images/unActiveUp.png");
}
.iconDown {
background-image: url("../../assets/images/unActiveDown.png");
}
}
.activeItem {
background-color: rgba(5, 95, 194, 1);
.infoWrap {
.name {
color: rgba(255, 255, 255, 1);
}
}
.text {
color: rgba(255, 255, 255, 1);
}
}
}
</style>
<template>
<div class="influenceContainer">
<div class="row">
<div class="buttonWrap">
<div
class="buttonItem"
:class="{ activeButtonItem: activeButtonId == item.id }"
v-for="item in buttonList"
:key="item.id"
@click="setActiveButtonId(item.id)"
>
{{ item.text }}
<div class="icon"></div>
</div>
</div>
<div class="panelWrap">
<InfluencePanel1 v-if="activeButtonId === 1"></InfluencePanel1>
<InfluencePanel2 v-if="activeButtonId === 2"></InfluencePanel2>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import InfluencePanel1 from "../components/influencePanel1.vue";
import InfluencePanel2 from "../components/influencePanel2.vue";
const buttonList = [
{
id: 1,
text: "对华科研影响"
},
{
id: 2,
text: "对华产业影响"
}
];
const activeButtonId = ref(buttonList[0].id);
const setActiveButtonId = id => {
activeButtonId.value = id;
};
</script>
<style lang="scss" scoped>
.influenceContainer {
width: 1920px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
margin-top: 15px;
}
.buttonWrap {
display: flex;
flex-direction: column;
width: 160px;
align-items: center;
gap: 15px;
.buttonItem {
width: 152px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
border-radius: 20px;
cursor: pointer;
.icon {
width: 8px;
height: 8px;
margin-left: 5px;
background-image: url("../../assets/images/whiteRriangle.png");
background-size: 100% 100%;
}
}
.activeButtonItem {
color: rgba(255, 255, 255, 1);
background-color: rgba(5, 95, 194, 1);
}
}
.panelWrap {
flex: 1;
}
</style>
<template>
<div class="overviewContainer">
<div class="row">
<CardCustom title="实体清单发布机构" :style="{ width: '984px', height: '284px' }">
<div class="panel1">
<img :src="organizationInfo.img" alt="" class="img" />
<div class="infoWrap">
<div class="item">
<div class="name">机构名称:</div>
<div class="doc">{{ organizationInfo.mingcheng }}</div>
</div>
<div class="item">
<div class="name">相关措施:</div>
<div class="doc">{{ organizationInfo.cuoshi }}</div>
</div>
<div class="item">
<div class="name">机构职责:</div>
<div class="doc zhize">{{ organizationInfo.zhize }}</div>
</div>
</div>
</div>
</CardCustom>
<CardCustom title="重点人物" :style="{ width: '600px', height: '284px' }">
<div class="panel2">
<div class="item" v-for="(item, index) in personLis" :key="index" @click="handlePerClick(item)">
<img :src="item.img" alt="" class="img" />
<div class="infoWrap">
<div class="name">{{ item.name }}</div>
<div class="enName">{{ item.enName }}</div>
<div class="party">{{ item.position }}</div>
</div>
</div>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="实体清单分布" :style="{ width: '984px', height: '520px' }">
<template #right>
<div class="panel3ButtonWrap">
<div
class="leftButton"
:class="{ activeButton: panel3ActiveIndex === 1 }"
@click="setPanel3ActiveIndex(1)"
>
领域类别分布
</div>
<div
class="rightButton"
:class="{ activeButton: panel3ActiveIndex === 2 }"
@click="setPanel3ActiveIndex(2)"
>
区域分布
</div>
</div>
<div class="panel3Jinbi"></div>
</template>
<div class="panel3">
<div class="chartWrap">
<PieCharts v-if="panel3ActiveIndex === 1"></PieCharts>
<MapCharts v-if="panel3ActiveIndex === 2" :date="route.query.startTime"></MapCharts>
</div>
<Hint text="本次制裁共新增83个实体,其中53个中国大陆实体、1个中国台湾实体。"></Hint>
</div>
</CardCustom>
<CardCustom title="制裁原因" :style="{ width: '600px', height: '520px' }">
<div class="panel4">
<div class="item" v-for="(item, index) in sanReasonSelect" :key="index">
<div class="icon1">{{ index + 1 }}</div>
<div class="text">{{ item.text }}</div>
<div class="icon2"></div>
</div>
</div>
</CardCustom>
</div>
<div class="row">
<CardCustom title="实体清单列表" :style="{ width: '984px', height: '678px' }">
<template #right>
<el-checkbox v-model="panel5IsChecked" label="只看中国实体" size="large" :style="{ marginRight: '15px' }" />
<!-- <ButtonList
:list="panel5ButtonList"
:gap="8"
:active-id="panel5ButtonAcitveID"
@click="panel5SetButtonAcitveID"
:style="{ marginRight: '15px' }"
></ButtonList> -->
</template>
<div class="panel5">
<div class="hintWrap">
<div class="title">
共计
<span class="text1">{{ entitiesCountBy50PercentRules?.totalCount }}</span>
家,其中50%规则涉及
<span class="text2">{{ entitiesCountBy50PercentRules?.ruleCount }}</span>
</div>
</div>
<div class="tableWrap" ref="tableWrapRef" @scroll="handleScroll">
<el-table
:data="selectEntitiesList"
class="sanction-table"
stripe
empty-text="暂无数据"
header-row-class-name="table-header"
row-class-name="table-row"
>
<el-table-column prop="name" label="实体清单" min-width="180">
<template #default="{ row }">
<div style="font-weight: 500" class="name" @click="handleOrgClick(row)">
<img v-if="row.img" :src="row.img" alt="" class="img" />
<div v-else class="imgUndefined">
{{ row.name?.match(/[\u4e00-\u9fa5a-zA-Z0-9]/)?.[0] }}
</div>
{{ row.name }}
</div>
</template>
</el-table-column>
<el-table-column prop="domains" label="涉及领域" width="180">
<template #default="{ row }">
<div class="domain-tags">
<el-tag v-for="tag in row.domains" :key="tag" :type="panel5TypeMap[tag]">{{
tag
}}</el-tag>
</div>
</template>
</el-table-column>
<!-- <el-table-column prop="address" label="上市地点" width="90" align="center">
<template #default="{ row }">
{{ row.address }}
</template>
</el-table-column> -->
<el-table-column prop="time" label="制裁时间" width="150" align="center">
<template #default="{ row }">
{{ row.time }}
</template>
</el-table-column>
<!-- <el-table-column prop="revenue" label="营收(亿元)" width="100" align="center">
<template #default="{ row }">
{{ row.revenue }}
</template>
</el-table-column> -->
<el-table-column prop="subCompany" label="50%规则子企业" min-width="140" align="center">
<template #default="{ row }">
<span class="subCompany">
{{ row.subCompany }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</CardCustom>
<CardCustom title="美国前序相关制裁、前序相关事件列表" :style="{ width: '600px', height: '678px' }">
<div class="panel6">
<div class="panel6-list">
<div class="item" v-for="(item, idx) in panel6" :key="item.title">
<div class="left">
<div class="icon"></div>
<div class="line"></div>
</div>
<div class="right">
<div class="date">{{ item.time }}</div>
<div class="title">{{ item.title }}</div>
<div class="desc">{{ item.desc }}</div>
</div>
</div>
</div>
<div class="more" @click="handleMoreClick">
查看更多
<span class="moreIcon"></span>
</div>
</div>
</CardCustom>
</div>
</div>
</template>
<script setup>
import CardCustom from "../../components/CardCustom.vue";
import PieCharts from "../components/pieCharts.vue";
import MapCharts from "../components/mapCharts.vue";
import ButtonList from "@/components/buttonList/buttonList.vue";
import Hint from "../components/hint.vue";
import { onMounted, reactive, ref, shallowRef, watch } from "vue";
import panel1_1 from "../../assets/images/panel1_1.png";
import {
getOrganizationInfo,
getPersonList,
getSanReasonSelect,
getSelectEntitiesList,
getEntitiesList,
getEntitiesCountBy50PercentRules,
getPrecedingEvents
} from "@/api/exportControl";
import _ from "lodash";
import { useRoute, useRouter } from "vue-router";
import { formatAnyDateToChinese } from "../../utils";
import { useGotoCompanyPages } from "@/router/modules/company";
const gotoCompanyPages = useGotoCompanyPages();
const route = useRoute();
const router = useRouter();
const organizationInfo = shallowRef({});
const personLis = shallowRef([]);
const sanReasonSelect = shallowRef([
{
text: "获取美国产物项,以支持中国量子技术;"
},
{
text: "支持2023年2月飞越美国上空的高空气球;"
},
{
text: "获取获取美国原产的无人机物项供中国军方使用。"
},
{
text: "法案序言称“中国光伏、风电企业通过政府补贴倾销产品”"
},
{
text: "将中国定位为“通过强制技术转让获取先进制程的威胁”"
}
]);
const handlePerClick = item => {
console.log(item);
const route = router.resolve({
path: "/characterPage",
query: {
type: item.type || [1, 2, 3][Math.floor(Math.random() * 3)]
}
});
window.open(route.href, "_blank");
};
const panel5IsChecked = ref(true);
const selectEntitiesList = shallowRef([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const loading = ref(false);
const tableWrapRef = ref(null);
const noMoreData = ref(false);
const entitiesCountBy50PercentRules = ref({
ruleCount: 0,
totalCount: 0
});
const precedingEvents = shallowRef([]);
const panel6 = ref([]);
const panel6CurrentPage = ref(1);
const panel6PageSize = ref(10);
const panel6Loading = ref(false);
const panel6NoMoreData = ref(false);
onMounted(async () => {
try {
const [organizationInfoData, sanReasonSelectData, entitiesCountBy50PercentRulesData, precedingEventsData] =
await Promise.all([
getOrganizationInfo(),
// getPersonList(),
getSanReasonSelect(route.query.startTime),
getEntitiesCountBy50PercentRules("实体清单", currentPage.value, pageSize.value),
// getSelectEntitiesList(route.query.startTime),
getPrecedingEvents(route.query.startTime)
]);
console.log("entitiesCountBy50PercentRulesData", entitiesCountBy50PercentRulesData);
entitiesCountBy50PercentRules.value = {
ruleCount: entitiesCountBy50PercentRulesData?.ruleCount || 0,
totalCount: entitiesCountBy50PercentRulesData?.totalCount || 0
};
organizationInfo.value = {
img: panel1_1,
mingcheng: organizationInfoData?.orgNameZh,
cuoshi: organizationInfoData?.cuoshi || "出口管制条例(EAR)、实体清单、商业管制清单(CCL)、232调查等",
zhize: organizationInfoData?.orgDuty || "--"
};
personLis.value = _.map(organizationInfoData?.personList, item => {
return {
name: item.name,
enName: item.enName,
position: item.position,
party: item.party,
img: item.imageUrl
};
});
sanReasonSelect.value = _.map(sanReasonSelectData, item => {
return { text: item };
});
precedingEvents.value = precedingEventsData.content || [];
panel6.value = precedingEventsData.content.map(item => ({
...item,
time: formatAnyDateToChinese(item.eventTime),
desc: item.summary || "--"
}));
// 初始化加载第一页数据
await fetchEntitiesList(currentPage.value, pageSize.value);
} catch (err) {
console.log(err);
}
});
// 获取实体清单数据
const entityTotal = ref(0);
const fetchEntitiesList = async (page = 1, size = 10) => {
if (loading.value || noMoreData.value) return;
loading.value = true;
try {
const res = await getEntitiesList("实体清单", page, size, route.query.startTime, panel5IsChecked.value);
if (res) {
entityTotal.value = res.totalElements;
const newData = res.content.map(item => ({
...item,
name: item.entityNameZh,
enName: item.entityName,
domains: item.techDomains,
time: formatAnyDateToChinese(item.startTime),
address: "--",
subCompany: "--",
img: ""
}));
// 如果是第一页,替换数据;否则追加数据
if (page === 1) {
selectEntitiesList.value = newData;
} else {
selectEntitiesList.value = [...selectEntitiesList.value, ...newData];
}
total.value = res.totalElements;
currentPage.value = res.number + 1;
// 检查是否还有更多数据
if (selectEntitiesList.value.length >= total.value) {
noMoreData.value = true;
}
}
} catch (err) {
console.error(err);
if (currentPage.value > 1) {
currentPage.value--;
}
} finally {
loading.value = false;
}
};
watch(
() => panel5IsChecked.value,
newVal => {
fetchEntitiesList(1, 10);
// getEntitiesCountBy50PercentRules("实体清单", 1, pageSize.value)
}
);
// 处理滚动事件
const debounceFlag = ref(false);
const handleScroll = () => {
if (!tableWrapRef.value || debounceFlag.value) return;
const { scrollTop, scrollHeight, clientHeight } = tableWrapRef.value;
// 当距离底部小于50px时加载更多
if (scrollHeight - scrollTop - clientHeight < 50) {
// 设置防抖标志
debounceFlag.value = true;
loadMore();
// 延迟重置防抖标志,防止连续触发
setTimeout(() => {
debounceFlag.value = false;
}, 300);
}
};
// 加载更多数据
const loadMore = () => {
if (!loading.value && !noMoreData.value) {
currentPage.value++;
fetchEntitiesList(currentPage.value, pageSize.value);
}
};
const panel3ActiveIndex = ref(1);
const setPanel3ActiveIndex = index => {
panel3ActiveIndex.value = index;
};
const panel5ButtonList = [
{ id: 1, text: "新增实体" },
{ id: 2, text: "移除实体" }
];
const panel5ButtonAcitveID = ref(panel5ButtonList[0].id);
const panel5SetButtonAcitveID = id => {
panel5ButtonAcitveID.value = id;
};
const panel5TypeMap = {
人工智能: "danger",
通信网络: "warning",
航空航天: "success"
};
const handleOrgClick = item => {
console.log(item);
if (item.entityType != 2) return;
// const route = router.resolve({
// name: "companyPages",
// params: {
// id: item.id
// }
// });
// window.open(route.href, "_blank");
gotoCompanyPages(item.id);
};
// 处理"查看更多"点击事件
const handleMoreClick = async () => {
if (panel6Loading.value || panel6NoMoreData.value) return;
panel6Loading.value = true;
try {
// 增加页码
panel6CurrentPage.value++;
// 获取更多数据
const moreEvents = await getPrecedingEvents(route.query.startTime, panel6CurrentPage.value, panel6PageSize.value);
if (moreEvents && Array.isArray(moreEvents.content) && moreEvents.content.length > 0) {
// 处理新数据
const newEvents = moreEvents.content.map(item => ({
...item,
time: formatAnyDateToChinese(item.eventTime),
desc: item.summary || "--"
}));
// 追加到现有数据
panel6.value = [...panel6.value, ...newEvents];
// 检查是否还有更多数据(简单的检查方式)
// 如果返回的数据少于请求的数量,则认为没有更多数据
if (newEvents.length < panel6PageSize.value) {
panel6NoMoreData.value = true;
}
} else {
// 没有更多数据
panel6NoMoreData.value = true;
}
} catch (error) {
console.error("获取更多前序事件失败:", error);
// 出错时回退页码
panel6CurrentPage.value--;
} finally {
panel6Loading.value = false;
}
};
</script>
<style lang="scss" scoped>
.overviewContainer {
width: 1600px;
margin: 0px auto 0 auto;
padding-bottom: 20px;
}
.row {
display: flex;
flex-direction: row;
gap: 16px;
margin-top: 15px;
}
.panel1 {
height: 100%;
padding: 2px 35px 0 24px;
display: flex;
.img {
width: 90px;
height: 90px;
margin-right: 16px;
}
.infoWrap {
flex: 1;
display: flex;
flex-direction: column;
gap: 19px;
.item {
display: flex;
.name {
flex-shrink: 0;
width: 100px;
color: rgba(59, 65, 75, 1);
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.doc {
flex: 1;
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 700;
line-height: 24px;
}
.zhize {
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5; /* 控制行数 */
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.panel2 {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
height: 100%;
overflow: auto;
align-content: flex-start;
gap: 16px;
padding: 0 23px 20px;
.item {
width: 268px;
height: 94px;
padding: 10px 16px;
background: rgba(231, 243, 255, 0.5);
display: flex;
align-items: center;
cursor: pointer;
.img {
width: 70px;
height: 70px;
margin-right: 16px;
}
.infoWrap {
display: flex;
flex-direction: column;
flex: 1;
max-height: 100%;
gap: 3px;
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
.name {
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 700;
line-height: 22px;
}
}
}
}
.panel3 {
height: 100%;
display: flex;
flex-direction: column;
padding: 3px 24px 10px;
.chartWrap {
flex: 1;
}
}
.panel3ButtonWrap {
flex: 1;
display: flex;
justify-content: center;
margin-right: 18px;
.leftButton {
width: 140px;
height: 34px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
border-radius: 40px 0 0 40px;
border: 2px solid rgba(5, 95, 194, 1);
border-right: none;
cursor: pointer;
}
.rightButton {
width: 140px;
height: 34px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
border-radius: 0px 40px 40px 0px;
border: 2px solid rgba(5, 95, 194, 1);
border-left: none;
cursor: pointer;
}
.activeButton {
background-color: rgba(5, 95, 194, 1);
color: #ffffff;
}
}
.panel3Jinbi {
width: 28px;
height: 28px;
background-image: url("../../assets/images/panel3_1.png");
background-size: 100% 100%;
margin-right: 4px;
cursor: pointer;
}
.panel4 {
display: flex;
flex-direction: column;
gap: 8px;
align-items: center;
.item {
width: 552px;
height: 48px;
display: flex;
align-items: center;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 2px;
.icon1 {
width: 24px;
height: 24px;
border-radius: 12px;
background-color: rgba(231, 243, 255, 1);
display: flex;
justify-content: center;
align-items: center;
margin-left: 15px;
margin-right: 15px;
font-size: 12px;
color: rgb(5, 95, 194);
font-weight: 500;
}
.text {
color: rgba(59, 65, 75, 1);
font-size: 16px;
font-weight: 400;
line-height: 30px;
flex: 1;
overflow: hidden;
white-space: noWrap;
text-overflow: ellipsis;
min-width: 0;
}
.icon2 {
width: 16px;
height: 16px;
background-image: url("../../assets/images/panel4_1.png");
background-size: 100% 100%;
margin-left: 15px;
margin-right: 15px;
}
}
}
.panel5 {
height: 100%;
display: flex;
flex-direction: column;
padding: 3px 24px 10px;
:deep(.table-header) {
color: rgba(59, 65, 75, 1);
font-size: 16px;
font-weight: 700;
height: 64px;
}
:deep(.table-row) {
height: 60px;
font-size: 16px;
font-weight: 400;
}
.hintWrap {
height: 40px;
display: flex;
align-items: center;
padding: 0 12px;
border: 1px solid rgb(231, 241, 255);
border-radius: 4px;
background: rgb(246, 251, 255);
.title {
color: var(--color-main-active);
font-size: 14px;
font-weight: 400;
line-height: 24px;
.text1 {
color: rgb(206, 79, 81);
font-weight: 900;
}
.text2 {
color: rgb(255, 149, 77);
font-weight: 900;
}
}
}
.tableWrap {
flex: 1;
margin-top: 14px;
min-height: 0;
overflow: auto;
.domain-tags {
display: flex;
gap: 4px;
}
}
.name {
display: flex;
align-items: center;
cursor: pointer;
.img {
width: 40px;
height: 40px;
margin-right: 6px;
margin-left: 6px;
flex-shrink: 0;
}
.imgUndefined {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
color: rgba(5, 95, 194, 1);
background-color: rgb(236, 245, 255);
border-radius: 20px;
margin-right: 6px;
margin-left: 6px;
}
}
.subCompany {
color: rgba(5, 95, 194, 1);
}
}
.panel6 {
height: 100%;
padding: 6px 45px 20px 28px;
display: flex;
flex-direction: column;
align-items: center;
.panel6-list {
height: 90%;
overflow-y: auto;
margin-bottom: auto;
padding-left: 5px;
}
.item {
display: flex;
.left {
position: relative;
margin-right: 20px;
.icon {
position: absolute;
left: -5px;
top: 7px;
width: 10px;
height: 10px;
background-image: url("../../assets/images/panel6_1.png");
background-size: 100% 100%;
}
.line {
width: 1px;
background-color: rgba(174, 214, 255, 1);
height: 100%;
}
}
.right {
flex: 1;
display: flex;
flex-direction: column;
padding-bottom: 40px;
.date {
font-size: 16px;
font-weight: 700;
line-height: 24px;
color: rgba(5, 95, 194, 1);
}
.title {
color: rgba(5, 95, 194, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-top: 12px;
}
.desc {
color: rgba(95, 101, 108, 1);
font-size: 16px;
font-weight: 400;
line-height: 24px;
margin-top: 8px;
}
}
}
.more {
display: flex;
color: rgba(5, 95, 194, 1);
font-size: 14px;
font-weight: 400;
line-height: 22px;
align-items: center;
cursor: pointer;
.moreIcon {
width: 16px;
height: 16px;
background-image: url("../../assets/images/panel6_more.png");
margin-left: 3px;
background-size: 100% 100%;
margin-bottom: 2px;
}
}
}
</style>
<template> <template>
<div class="page-container"> <div class="layout-container">
<Header /> <!-- 导航菜单 -->
<!-- 导航标签区域 --> <div class="layout-main">
<div class="nav-section"> <div class="layout-main-header">
<div class="tabs"> <div class="layout-main-header-left-box">
<div :class="['tab-item', { active: activeTab === '制裁概况' }]" @click="setActiveTab('制裁概况')">制裁概况</div> <div class="left-box-top">
<div :class="['tab-item', { active: activeTab === '深度挖掘' }]" @click="setActiveTab('深度挖掘')">深度挖掘</div> <div class="icon">
<div :class="['tab-item', { active: activeTab === '影响分析' }]" @click="setActiveTab('影响分析')">影响分析</div> <img src="../assets/images/USA-logo.png" alt="" />
</div>
<div class="action-buttons">
<el-button>法案原文</el-button>
<el-button type="primary">分析报告</el-button>
</div>
</div>
<!-- 内容区域 -->
<div class="content-section">
<!-- <div class="content-placeholder">
<el-icon><document /></el-icon>
<p>这里是制裁概况的内容区域</p>
<p>请选择上方标签查看不同分析内容</p>
</div> -->
<Survey v-if="activeTab === '制裁概况'" />
<DepthMine v-if="activeTab === '深度挖掘'" />
<ImpactAnalysis v-if="activeTab === '影响分析'" />
</div> </div>
<div class="info">
<div class="left-absolute"> <div class="info-box1">{{ "实体清单新增内容" }}</div>
<el-image :src="Left1" alt="" /> <div class="info-box2">
<el-image :src="Left2" alt="" /> <div class="info-box2-item">{{ "2025-19508 (90 FR 48193)" }}</div>
<el-image :src="Left3" alt="" /> </div>
</div>
</div>
<div class="left-box-bottom">
<div
class="left-box-bottom-item"
:class="{ leftBoxBottomItemActive: activeTitle === item.name }"
v-for="(item, index) in mainHeaderBtnList"
:key="index"
@click="handleClickMainHeaderBtn(item)"
>
<div class="icon">
<img v-if="activeTitle === item.name" :src="item.activeIcon" alt="" />
<img v-else :src="item.icon" alt="" />
</div>
<div class="name" :class="{ nameActive: activeTitle === item.name }">
{{ item.name }}
</div>
</div>
</div>
</div>
<div class="layout-main-header-right-box">
<div class="right-box-top">
<div class="time">{{ route.query.startTime }}</div>
<div class="name">{{ "美国商务部工业与安全局" }}</div>
</div>
<div class="right-box-bottom">
<el-button
type="plain"
size="large"
disabled
icon="Search"
@click="handleSwitchActiveName('实体清单原文')"
>实体清单原文</el-button
>
<el-button type="primary" size="large" disabled icon="EditPen">分析报告</el-button>
</div>
</div>
</div>
<div class="layout-main-center">
<router-view />
</div>
</div>
<div class="layout-report-box" v-if="activeName === '实体清单原文'">
<div class="report-close" @click="handleSwitchActiveName('分析报告')">
<img src="../assets/images/report-close-icon.png" alt="" />
</div>
<div class="report-main">
<div class="report-header">
<div class="report-header-left">
<div class="text">法案版本:</div>
<div class="select-box">
<el-select v-model="curBill" placeholder="选择法案" style="width: 240px">
<el-option v-for="item in billList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<div class="report-header-right">
<div class="btn">
<div class="icon">
<img src="../assets/images/report-header-icon1.png" alt="" />
</div>
<div class="text">翻译</div>
</div> </div>
<div class="btn">
<div class="icon">
<img src="../assets/images/report-header-icon2.png" alt="" />
</div>
<div class="text">下载</div>
</div>
<div class="btn">
<div class="icon">
<img src="../assets/images/report-header-icon3.png" alt="" />
</div>
<div class="text">对比</div>
</div>
<div class="btn">
<div class="icon">
<img src="../assets/images/report-header-icon4.png" alt="" />
</div>
<div class="text">查找</div>
</div>
</div>
</div>
<div class="report-content">
<div class="content-left">
<img src="../assets/images/report1.png" alt="" />
</div>
<div class="content-right">
<img src="../assets/images/report2.png" alt="" />
</div>
</div>
</div>
</div>
<!-- <div class="tool-box">
<div class="tool1">
<img src="./assets/icons/tool-icon1.png" alt="" />
</div>
<div class="tool2">
<img src="./assets/icons/tool-icon2.png" alt="" />
</div>
<div class="tool3">
<img src="./assets/icons/tool-icon3.png" alt="" />
</div>
</div> -->
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref, onMounted } from "vue";
import Header from "./header.vue"; import router from "@/router";
import Survey from "./content/survey.vue";
import DepthMine from "./content/depthMine.vue"; import icon1 from "../assets/icons/icon1.png";
import ImpactAnalysis from "./content/impactAnalysis.vue"; import icon1Active from "../assets/icons/icon1_active.png";
import icon2 from "../assets/icons/icon2.png";
import Left1 from "@/assets/images/left-1.png"; import icon2Active from "../assets/icons/icon2_active.png";
import Left2 from "@/assets/images/left-2.png"; import icon3 from "../assets/icons/icon3.png";
import Left3 from "@/assets/images/left-3.png"; import icon3Active from "../assets/icons/icon3_active.png";
import { useRoute } from "vue-router";
const activeTab = ref("制裁概况"); const route = useRoute();
const setActiveTab = tabName => { const activeName = ref("分析报告");
activeTab.value = tabName;
const handleSwitchActiveName = name => {
activeName.value = name;
}; };
</script>
<style scoped lang="scss"> const curBill = ref("公法(2025年7月4日)");
.page-container {
position: relative;
margin: 0px auto;
background-color: rgba(247, 248, 249, 1);
}
.nav-section {
display: flex;
justify-content: space-between;
align-items: center;
/* padding: 16px 24px; */
padding-left: 6%;
padding-right: 6%;
background-color: #fff;
border-bottom: 1px solid #ebeef5;
}
.tabs { const billList = ref([
display: flex; {
gap: 0; label: "公法(2025年7月4日)",
} value: "公法(2025年7月4日)"
},
{
label: "公法(2025年7月2日)",
value: "公法(2025年7月2日)"
}
]);
.tab-item { const mainHeaderBtnList = ref([
width: 120px; {
height: 40px; icon: icon1,
text-align: center; activeIcon: icon1Active,
line-height: 30px; name: "制裁概况",
// padding: 12px 24px; path: "/exportControlAnalysis/overview"
font-size: 18px; },
color: #606266; {
cursor: pointer; icon: icon2,
border-bottom: 3px solid transparent; activeIcon: icon2Active,
transition: all 0.3s; name: "深度挖掘",
box-sizing: border-box; path: "/exportControlAnalysis/deepDig"
} },
{
icon: icon3,
activeIcon: icon3Active,
name: "影响分析",
path: "/exportControlAnalysis/influence"
}
]);
// .tab-item.active { const activeTitle = ref("制裁概况");
// color: #409eff;
// border-bottom-color: #409eff;
// background-color: rgba(64, 158, 255, 0.1);
// }
.tab-item.active { const handleClickMainHeaderBtn = item => {
color: var(--color-main-active); activeTitle.value = item.name;
font-family: Microsoft YaHei; // window.sessionStorage.setItem('activeTitle', activeTitle.value)
font-size: 18px; router.push({
font-weight: 700; path: item.path,
line-height: 30px; query: {
border-bottom: 3px solid var(--color-main-active); ...route.query
} }
});
};
.tab-item:hover { const activeNavIndex = ref(0);
color: var(--color-main-active);
}
.action-buttons { const handleClickNav = (index, item) => {
display: flex; activeNavIndex.value = index;
gap: 12px; router.push(item.path);
} };
.content-section { onMounted(() => {
padding: 24px; // if (window.sessionStorage.getItem("activeTitle")) {
min-height: 400px; // activeTitle.value = window.sessionStorage.getItem("activeTitle");
} // }
});
</script>
.content-placeholder { <style lang="scss" scoped>
display: flex; .layout-container {
flex-direction: column; width: 1920px;
align-items: center; height: 1016px;
justify-content: center; background: rgba(249, 250, 252, 1);
height: 300px; position: relative;
color: #909399; margin: 0 auto;
} // .layout-header {
// width: 1920px;
// height: 64px;
// // background: #0a121e;
// background: #fff;
// // border-bottom: 1px solid rgba(0, 0, 0, 0.1);
// box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1);
// // margin-bottom: 13px;
// display: flex;
// position: relative;
// z-index: 99;
// .layout-header-left {
// width: 480px;
// display: flex;
// .logo-box {
// width: 36px;
// height: 36px;
// margin: 14px;
.content-placeholder .el-icon { // img {
font-size: 48px; // width: 100%;
margin-bottom: 16px; // height: 100%;
color: #dcdfe6; // border-radius: 6px;
} // }
// }
// .title-box {
// height: 64px;
// width: 350px;
// margin: 0 5px;
// color: #eee;
// line-height: 64px;
// font-size: 20px;
// font-weight: bold;
// color: rgba(10, 18, 30, 1);
// font-family: Microsoft YaHei;
// font-size: 22px;
// font-weight: 700;
// text-align: left;
// }
// }
// .layout-header-center {
// width: 700px;
// display: flex;
// justify-content: space-between;
// margin-left: 200px;
// .nav-item {
// border-radius: 5px;
// cursor: pointer;
// display: flex;
// &:hover {
// background: rgba(255, 255, 255, 0.2);
// }
// .nav-icon-box {
// width: 25px;
// height: 25px;
// margin: 22px 0 20px 5px;
// .nav-icon {
// width: 20px;
// height: 20px;
// img {
// width: 100%;
// height: 100%;
// }
// }
// .nav-icon-active {
// width: 20px;
// height: 20px;
// img {
// width: 100%;
// height: 100%;
// }
// }
// }
// .name-box {
// margin: 18px 5px;
// height: 30px;
// text-align: center;
// line-height: 30px;
// color: rgba(59, 65, 75, 1);
// letter-spacing: 2px;
// font-size: 18px;
// }
// // .nameActive {
// // color: #ea9518;
// // font-weight: bold;
// // }
// }
// .navItemActive {
// // background: #295dab;
// // border-bottom: 4px solid #ea9518;
// // &:hover {
// // background: #295dab;
// // border-bottom: 4px solid #ea9518;
// // }
// }
// }
// .layout-header-right {
// flex: 1;
// display: flex;
// justify-content: flex-end;
.content-placeholder p { // .nav-search {
font-size: 16px; // width: 22px;
margin-top: 8px; // height: 22px;
} // margin: 21px 0;
// }
@media (max-width: 768px) { // .nav-message {
.bill-info { // width: 22px;
flex-direction: column; // height: 22px;
align-items: flex-start; // margin: 21px 30px;
} // }
.date-author { // .nav-usr {
align-items: flex-start; // width: 110px;
margin-top: 12px; // display: flex;
// height: 40px;
// margin: 12px 5px 12px 0;
// .usr-img {
// margin-top: 4px;
// height: 32px;
// width: 32px;
// background: rgba(255, 255, 255, 0.5);
// border-radius: 100%;
// box-sizing: border-box;
// padding: 4px;
// // img {
// // width: 100%;
// // height: 100%;
// // }
// }
// .usr-info {
// height: 40px;
// line-height: 40px;
// text-align: center;
// font-size: 14px;
// margin-left: 10px;
// }
// }
// }
// }
.layout-main {
width: 100%;
height: 100%;
.layout-main-header {
height: 137px;
background: rgba(255, 255, 255, 1);
display: flex;
justify-content: space-between;
.layout-main-header-left-box {
width: 800px;
margin-left: 160px;
margin-top: 13px;
.left-box-top {
height: 64px;
display: flex;
.icon {
width: 64px;
height: 64px;
} }
.info {
.nav-section { margin-left: 9px;
flex-direction: column; .info-box1 {
align-items: flex-start; color: rgba(59, 65, 75, 1);
gap: 16px; font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 600;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
margin-top: 5px;
} }
.info-box2 {
.tabs { margin-top: 5px;
height: 22px;
line-height: 22px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
display: flex;
margin-left: -10px;
.info-box2-item {
padding: 0 10px;
}
}
}
}
.left-box-bottom {
display: flex;
height: 40px;
margin-top: 21px;
.left-box-bottom-item {
display: flex;
align-items: center;
margin-right: 32px;
margin-top: 3px;
height: 35px;
border-bottom: 2px solid transparent;
cursor: pointer;
// &:hover{
// .name {
// color: rgba(22, 119, 255, 1);
// }
// }
.icon {
margin-top: 1px;
width: 16px;
height: 16px;
img {
width: 100%; width: 100%;
overflow-x: auto; height: 100%;
} }
} }
.name {
.left-absolute { color: rgba(95, 101, 108, 1);
position: fixed; font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
margin-left: 3px;
}
.nameActive {
color: rgba(20, 89, 187, 1);
font-weight: 700;
}
}
.leftBoxBottomItemActive {
border-bottom: 2px solid rgba(20, 89, 187, 1);
}
}
}
.layout-main-header-right-box {
width: 300px;
margin-right: 160px;
margin-top: 19px;
.right-box-top {
.time {
height: 24px;
line-height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
.name {
height: 24px;
line-height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
}
.right-box-bottom {
margin-top: 24px;
text-align: right;
}
}
}
.layout-main-center {
overflow-x: hidden;
}
}
.layout-report-box {
position: absolute;
z-index: 9999;
top: 154px;
left: 0; left: 0;
top: 60%; width: 100%;
// width: 48px; height: 926px;
background: rgba(248, 249, 250, 1);
.report-close {
position: absolute;
top: 24px;
right: 178px;
width: 32px;
height: 32px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.report-main {
width: 1600px;
height: 926px;
margin: 0 auto;
background: #fff;
box-sizing: border-box;
padding: 0 69px;
.report-header {
height: 77px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
justify-content: space-between;
.report-header-left {
display: flex; display: flex;
flex-direction: column; .text {
margin-top: 32px;
width: 70px;
height: 14px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 14px;
}
.select-box {
margin-left: 8px;
margin-top: 23px;
}
}
.report-header-right {
display: flex;
margin-top: 24px;
.btn {
display: flex;
width: 88px;
height: 32px;
margin-left: 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
display: flex;
justify-content: center;
align-items: center; align-items: center;
gap: 16px; .icon {
background-color: #fff; width: 16px;
border-radius: 10px; height: 16px;
border-top-left-radius: 0; img {
border-bottom-left-radius: 0; width: 100%;
padding: 16px 12px; height: 100%;
box-shadow: 0 0 15px 0 rgba(60, 87, 126, 0.2);
div {
width: 17px;
height: 18px;
} }
}
.text {
margin-left: 8px;
height: 32px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 32px;
}
}
}
}
.report-content {
display: flex;
margin-top: 35px;
.content-left {
width: 680px;
height: 786px;
// background: #eee;
// overflow-y: auto;
img {
width: 100%;
height: 100%;
}
}
.content-right {
margin-left: 89px;
width: 680px;
height: 786px;
// background: #eee;
// overflow-y: auto;
img {
width: 100%;
height: 100%;
}
}
}
}
}
// .tool-box {
// position: fixed;
// z-index: 10000;
// bottom: 80px;
// left: 0;
// width: 48px;
// height: 144px;
// border-radius: 0px 10px 10px 0px;
// box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
// background: rgba(255, 255, 255, 1);
// .tool1 {
// width: 17px;
// height: 18px;
// margin-top: 17px;
// margin-left: 16px;
// cursor: pointer;
// img {
// width: 100%;
// height: 100%;
// }
// }
// .tool2 {
// width: 22px;
// height: 20px;
// margin-top: 26px;
// margin-left: 14px;
// cursor: pointer;
// img {
// width: 100%;
// height: 100%;
// }
// }
// .tool3 {
// width: 20px;
// height: 20px;
// margin-top: 25px;
// margin-left: 15px;
// cursor: pointer;
// img {
// width: 100%;
// height: 100%;
// }
// }
// }
} }
</style> </style>
<template>
<el-tooltip
effect="dark"
:content="content"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="text-ellipsis">
<slot>{{ content }}</slot>
</div>
</el-tooltip>
</template>
<script setup>
defineProps({
content: {
type: String,
default: ""
}
});
</script>
<style scoped>
.text-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
cursor: pointer;
}
</style>
<style>
.common-prompt-popper.el-popper {
padding: 8px 16px !important;
border-radius: 10px !important;
background-color: rgb(59, 65, 75) !important;
font-size: 16px !important;
font-weight: 400 !important;
font-family: "Microsoft YaHei" !important;
line-height: 30px !important;
color: #fff !important;
border: none !important;
}
.common-prompt-popper.el-popper .el-popper__arrow::before {
background-color: rgb(59, 65, 75) !important;
border-color: rgb(59, 65, 75) !important;
}
</style>
<template>
<el-card class="card">
<div class="wrap">
<div class="header" :style="{ height: headerHeight }">
<div class="iconWrap"></div>
<div class="titleWrap">
{{ title }}
</div>
<div class="rightWrap">
<slot name="right"> </slot>
<div class="ButtonWrap">
<div class="button buttonXiazai"></div>
<div class="button buttonShoucang"></div>
</div>
</div>
</div>
<div class="body">
<slot name="default"> </slot>
</div>
</div>
</el-card>
</template>
<script setup>
import { Edit, Plus } from "@element-plus/icons-vue";
const emit = defineEmits(["moreClick"]);
defineProps({
titleBackgroundColor: {
type: String,
default: "red"
},
titleColor: {
type: String,
default: "#fff"
},
titleTextAlign: {
type: String,
default: "center"
},
lineColor: {
type: String,
default: "transparent"
},
title: {
type: String,
default: "风险信号"
},
iconWidth: {
type: String,
default: "60px"
},
headerHeight: {
type: String,
default: "56px"
}
});
function moreClick() {
emit("moreClick");
}
</script>
<style lang="scss" scoped>
.card {
position: relative;
:deep(.el-card__body) {
padding: 0;
height: 100%;
}
}
.wrap {
height: 100%;
display: flex;
flex-direction: column;
}
.header {
height: 56px;
display: flex;
flex-direction: row;
align-items: center;
z-index: 1;
position: relative;
flex-shrink: 0;
padding-right: 12px;
}
.iconWrap {
width: 8px;
height: 16px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.titleWrap {
margin-left: 16px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-size: 20px;
font-weight: 600;
}
.rightWrap {
display: flex;
justify-content: end;
align-items: center;
flex: 1;
}
.ButtonWrap {
display: flex;
align-items: center;
.button {
width: 28px;
height: 28px;
background-size: 100% 100%;
cursor: pointer;
}
.buttonXiazai {
background-image: url("../assets/images/xiazaiIcon.png");
margin-right: 4px;
}
.buttonShoucang {
background-image: url("../assets/images/shoucangIcon.png");
}
}
.body {
flex: 1;
min-height: 0;
}
</style>
<template>
<el-dialog
v-model="visible"
width="1280px"
:show-close="false"
:close-on-click-modal="false"
class="rule-subsidiary-dialog"
destroy-on-close
top="5vh"
draggable
>
<template #header>
<div class="dialog-header">
<div class="left-title">
<img :src="defaultIcon" alt="" class="title-icon" />
<span class="company-name">{{ companyName }}</span>
<span class="suffix-text">“实体清单50%规则” 涉及实体列表</span>
</div>
<div class="right-actions">
<div class="right-count">
<span class="highlight">{{ totalCount }}</span>
</div>
<el-icon class="close-btn" @click="visible = false"><Close /></el-icon>
</div>
</div>
</template>
<div class="dialog-content">
<el-table
:data="tableData"
table-layout="fixed"
:row-class-name="tableRowClassName"
:header-cell-style="{ background: '#fff' }"
style="width: 100%"
>
<el-table-column label="实体名称" min-width="300">
<template #default="{ row }">
<div class="entity-name-cell">
<el-avatar class="avatar" :size="24" :src="row.avatar || defaultIcon" />
<div class="name" :title="row.name">{{ row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="涉及领域" width="200">
<template #default="{ row }">
<div class="domain-cell">
<el-tag
v-for="tag in row.domains"
:key="tag"
class="domain-tag"
effect="plain"
:disable-transitions="true"
:style="getTagStyle(tag)"
>
{{ tag }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column prop="equityRatio" label="股权占比" width="120" align="center" />
<el-table-column prop="location" label="上市地点" width="120" align="center" />
<el-table-column prop="revenue" label="营收(亿元)" width="120" align="center" />
</el-table>
<div class="dialog-footer">
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="totalCount"
layout="prev, pager, next"
prev-text="<"
next-text=">"
@current-change="handleCurrentChange"
/>
</div>
</div>
</el-dialog>
</template>
<script setup>
import { ref, defineProps, defineEmits, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import defaultIcon from "@/assets/icons/default-icon1.png";
const props = defineProps({
modelValue: {
type: Boolean,
default: false
},
companyName: {
type: String,
default: ""
},
totalCount: {
type: Number,
default: 0
},
dataList: {
type: Array,
default: () => []
}
});
const emit = defineEmits(["update:modelValue"]);
const visible = computed({
get: () => props.modelValue,
set: val => emit("update:modelValue", val)
});
const currentPage = ref(1);
const pageSize = ref(10);
const tableData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return props.dataList.slice(start, end).map(item => ({
...item,
name: item.orgName,
domains: item.techDomains || [],
equityRatio: item.equityRatio ? (item.equityRatio * 100).toFixed(2) + '%' : '--',
location: '--',
revenue: item.revenue || '--'
}));
});
const handleCurrentChange = val => {
currentPage.value = val;
};
const tableRowClassName = ({ rowIndex }) => {
return rowIndex % 2 === 0 ? "odd-row" : "";
};
const getTagStyle = tag => {
const colorPool = [
{ color: "#CD4246", background: "rgba(255, 242, 240, 1)", borderColor: "rgba(255, 163, 158, 1)" },
{ color: "#0E78F1", background: "rgba(230, 244, 255, 1)", borderColor: "rgba(145, 206, 255, 1)" },
{ color: "#722ED1", background: "rgba(249, 240, 255, 1)", borderColor: "rgba(211, 173, 247, 1)" },
{ color: "#13A8A8", background: "rgba(230, 255, 251, 1)", borderColor: "rgba(135, 232, 222, 1)" },
{ color: "#FA8C16", background: "rgba(255, 247, 230, 1)", borderColor: "rgba(255, 213, 145, 1)" },
{ color: "#52C41A", background: "rgba(246, 255, 237, 1)", borderColor: "rgba(183, 235, 143, 1)" }
];
let hash = 0;
for (let i = 0; i < tag.length; i++) {
hash = tag.charCodeAt(i) + ((hash << 5) - hash);
}
const index = Math.abs(hash) % colorPool.length;
return colorPool[index];
};
</script>
<style lang="scss" scoped>
.rule-subsidiary-dialog {
:deep(.el-dialog__header) {
margin-right: 0;
padding: 24px;
border-bottom: 1px solid #edeff2;
}
:deep(.el-dialog__body) {
padding: 0;
}
}
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
.left-title {
display: flex;
align-items: center;
.title-icon {
width: 24px;
height: 24px;
margin-right: 8px;
}
.company-name {
font-size: 18px;
font-weight: 700;
color: #3b414b;
margin-right: 8px;
font-family: "Microsoft YaHei";
}
.suffix-text {
font-size: 18px;
font-weight: 700;
color: #3b414b;
font-family: "Microsoft YaHei";
}
}
.right-actions {
display: flex;
align-items: center;
.right-count {
font-size: 14px;
color: #5f656c;
font-weight: 700;
font-family: "Microsoft YaHei";
margin-right: 20px;
.highlight {
color: #cd4246;
margin: 0 4px;
font-size: 16px;
}
}
.close-btn {
font-size: 20px;
color: #909399;
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #409eff;
}
}
}
}
.dialog-content {
height: 722px; /* Adjusted to fit total 851px approx with header (65px) and footer (64px) */
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 0 24px;
:deep(.el-table) {
--el-table-header-bg-color: #fff;
--el-table-border-color: transparent;
--el-table-row-hover-bg-color: rgba(248, 249, 250, 1);
}
:deep(.el-table__inner-wrapper::before) {
background-color: transparent;
}
:deep(.el-table__header-wrapper th) {
height: 60px;
background-color: #fff;
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
font-family: "Microsoft YaHei";
border-bottom: 1px solid rgba(230, 231, 232, 1);
}
:deep(.el-table__header-wrapper .cell) {
line-height: 22px;
}
:deep(.el-table__row) {
height: 64px;
}
:deep(.el-table__cell) {
border-bottom: 0;
font-size: 16px;
font-weight: 400;
color: rgb(95, 101, 108);
font-family: "Microsoft YaHei";
}
:deep(.odd-row td.el-table__cell) {
background-color: rgba(248, 249, 250, 1);
}
.entity-name-cell {
display: flex;
align-items: center;
.avatar {
flex: 0 0 24px;
margin-right: 9px;
}
.name {
font-size: 16px;
font-weight: 700;
color: rgba(59, 65, 75, 1);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.domain-cell {
display: flex;
align-items: center;
gap: 8px;
:deep(.el-tag) {
height: auto;
padding: 2px 8px;
border-radius: 4px;
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
}
}
}
.dialog-footer {
height: 64px;
display: flex;
justify-content: center;
align-items: center;
border-top: 1px solid #edeff2;
:deep(.el-pagination) {
--el-pagination-button-bg-color: #fff;
--el-pagination-hover-color: #0e78f1;
--el-pagination-font-size: 14px;
.el-pager li {
border: 1px solid #dcdfe6;
border-radius: 4px;
margin: 0 4px;
font-weight: 400;
color: #5f656c;
min-width: 32px;
height: 32px;
line-height: 30px;
&.is-active {
background-color: #0e78f1;
color: #fff;
border-color: #0e78f1;
}
&:hover:not(.is-active) {
color: #0e78f1;
border-color: #0e78f1;
}
}
.btn-prev,
.btn-next {
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 0;
margin: 0 4px;
min-width: 32px;
height: 32px;
line-height: 30px;
text-align: center;
&:hover {
color: #0e78f1;
border-color: #0e78f1;
}
&[disabled] {
border-color: #e4e7ed;
color: #c0c4cc;
}
}
}
}
</style>
\ No newline at end of file
<template> <template>
<div class="message-bubble"> <div class="message-bubble">
<div class="avatar-container"> <div class="avatar-container" @click="handleClick">
<img :src="avatar" :alt="name" class="avatar" /> <img :src="avatar || avatarUser" :alt="name" class="avatar" />
</div> </div>
<div class="bubble-container"> <div class="bubble-container">
<div class="bubble"> <div class="bubble">
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
<span class="name">{{ name }}</span> <span class="name">{{ name }}</span>
<span class="meta">{{ time }} · {{ source }}</span> <span class="meta">{{ time }} · {{ source }}</span>
</div> </div>
<div class="bubble-content"> <div class="bubble-content" @click="handleInfoClick">
{{ content }} {{ content }}
</div> </div>
<div class="triangle"></div> <div class="triangle"></div>
...@@ -19,6 +19,9 @@ ...@@ -19,6 +19,9 @@
</template> </template>
<script setup> <script setup>
import avatarUser from "@/assets/images/avatar_user.png";
const emit = defineEmits(["click", "info-click"]);
defineProps({ defineProps({
avatar: { avatar: {
type: String, type: String,
...@@ -42,17 +45,27 @@ defineProps({ ...@@ -42,17 +45,27 @@ defineProps({
"埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。" "埃隆·马斯克在强力支持我竞选总统之前,早就知道我强烈反对‘电动汽车强制令’。这太荒谬了,这一直是我竞选活动的主要部分。电动汽车没问题,但不应该强迫每个人都拥有一辆。埃隆获得的补贴可能远远超过历史上任何一个人。如果没有补贴,埃隆可能不得不关门大吉,回到南非老家。"
} }
}); });
const handleClick = () => {
emit("click");
};
const handleInfoClick = () => {
emit("info-click");
};
</script> </script>
<style scoped> <style scoped>
.message-bubble { .message-bubble {
display: flex; display: flex;
max-width: 700px; max-width: 720px;
margin: 20px 0; margin-top: 5px;
margin-bottom: 16px;
} }
.avatar-container { .avatar-container {
flex-shrink: 0; flex-shrink: 0;
flex-grow: 0;
flex: 0;
margin-right: 12px; margin-right: 12px;
} }
...@@ -71,7 +84,7 @@ defineProps({ ...@@ -71,7 +84,7 @@ defineProps({
.bubble { .bubble {
background-color: rgba(246, 250, 255, 1); background-color: rgba(246, 250, 255, 1);
border-radius: 12px; border-radius: 12px;
padding: 12px 16px; padding: 10px 16px;
position: relative; position: relative;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
} }
...@@ -80,13 +93,18 @@ defineProps({ ...@@ -80,13 +93,18 @@ defineProps({
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 8px; margin-bottom: 5px;
font-family: "微软雅黑";
} }
.name { .name {
font-weight: bold; color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px; font-size: 16px;
color: #333; font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
} }
.meta { .meta {
...@@ -107,6 +125,7 @@ defineProps({ ...@@ -107,6 +125,7 @@ defineProps({
line-height: 24px; line-height: 24px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: justify; text-align: justify;
font-family: "微软雅黑";
} }
.triangle { .triangle {
......
...@@ -7,12 +7,19 @@ ...@@ -7,12 +7,19 @@
<div class="sub-title">{{ subtitle }}</div> <div class="sub-title">{{ subtitle }}</div>
</div> </div>
<div class="description">{{ description }}</div> <div class="description">{{ description }}</div>
<div v-if="quantity > 0" class="quantity" :style="{ color: color }">{{ quantity }}</div> <div v-if="quantity > 0" class="quantity" :style="{ color: color }">
{{ quantity }}{{ unit || "个" }}
<div class="quantity-title-des" v-if="desMap[title]">
<img class="info-icon" :src="infoIcon" alt="" />
{{ desMap[title] }}
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import infoIcon from "../assets/icons/info-icon.png";
defineProps({ defineProps({
title: { title: {
type: String, type: String,
...@@ -30,17 +37,27 @@ defineProps({ ...@@ -30,17 +37,27 @@ defineProps({
type: [Number, String], type: [Number, String],
default: 0 default: 0
}, },
unit: {
type: String,
default: ""
},
color: { color: {
type: String, type: String,
default: "#409EFF" default: "#409EFF"
} }
}); });
const desMap = {
实体清单: "中国实体数量",
商业管制清单: "受管制物项数量",
关键与新兴技术清单: "关键与新兴技术数量"
};
</script> </script>
<style scoped> <style scoped lang="scss">
.info-card { .info-card {
max-width: 388px; flex: 1;
min-width: 300px; // max-width: 388px;
min-width: 200px;
height: 150px; height: 150px;
border-radius: 12px; border-radius: 12px;
background-color: white; background-color: white;
...@@ -49,6 +66,7 @@ defineProps({ ...@@ -49,6 +66,7 @@ defineProps({
position: relative; position: relative;
overflow: hidden; overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.3s ease; transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
} }
.info-card:hover { .info-card:hover {
...@@ -76,10 +94,15 @@ defineProps({ ...@@ -76,10 +94,15 @@ defineProps({
} }
.main-title { .main-title {
font-family: $base-font-family;
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
color: #1f2937; color: #1f2937;
line-height: 1.2; line-height: 1.2;
max-width: 70%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.sub-title { .sub-title {
...@@ -104,9 +127,27 @@ defineProps({ ...@@ -104,9 +127,27 @@ defineProps({
.quantity { .quantity {
position: absolute; position: absolute;
top: 20px; top: 15px;
right: 20px; right: 20px;
font-size: 32px; font-size: 32px;
font-weight: 700; font-weight: 700;
width: 150px;
display: flex;
flex-direction: column;
align-items: flex-end;
.quantity-title-des {
display: flex;
align-items: center;
font-family: Source Han Sans CN;
font-size: 14px;
font-weight: 400;
color: rgba(95, 101, 108, 1);
line-height: 1.4;
.info-icon {
width: 16px;
height: 16px;
margin-right: 4px;
}
}
} }
</style> </style>
...@@ -2,16 +2,27 @@ ...@@ -2,16 +2,27 @@
<div class="news-list"> <div class="news-list">
<div v-for="(item, index) in listData" :key="index" class="news-item" @click="handleItemClick(item)"> <div v-for="(item, index) in listData" :key="index" class="news-item" @click="handleItemClick(item)">
<div class="news-image"> <div class="news-image">
<img :src="item.image" :alt="item.title" /> <img :src="item.avatar || newsImg" :alt="item.title" />
</div> </div>
<div class="news-content"> <div class="news-content">
<div class="news-header"> <div class="news-header">
<div class="news-title">{{ item.title }}</div> <CommonPrompt :content="item.title" class="news-title-prompt">
<span class="news-title">{{ item.title }}</span>
</CommonPrompt>
<div class="news-meta"> <div class="news-meta">
<span class="news-time">{{ item.time }}</span> · <span class="news-time">{{ item.time }}</span> ·
<span class="news-source">{{ item.source }}</span> <span class="news-source">{{ item.source }}</span>
</div> </div>
</div> </div>
<!-- <el-tooltip
effect="dark"
:content="item.description"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="news-description">{{ item.description }}</div>
</el-tooltip> -->
<div class="news-description">{{ item.description }}</div> <div class="news-description">{{ item.description }}</div>
</div> </div>
</div> </div>
...@@ -19,6 +30,8 @@ ...@@ -19,6 +30,8 @@
</template> </template>
<script setup> <script setup>
import CommonPrompt from "../commonPrompt/index.vue";
import newsImg from "@/assets/images/news-img.png";
const props = defineProps({ const props = defineProps({
listData: { listData: {
type: Array, type: Array,
...@@ -90,15 +103,22 @@ const handleItemClick = item => { ...@@ -90,15 +103,22 @@ const handleItemClick = item => {
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
margin-bottom: 8px; margin-bottom: 8px;
overflow: hidden;
}
.news-title-prompt {
flex: 1;
min-width: 0;
margin-right: 12px;
} }
.news-title { .news-title {
font-family: "微软雅黑";
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
line-height: 24px; line-height: 24px;
flex: 1; display: inline-block;
margin-right: 12px;
} }
.news-meta { .news-meta {
...@@ -112,30 +132,17 @@ const handleItemClick = item => { ...@@ -112,30 +132,17 @@ const handleItemClick = item => {
} }
.news-time { .news-time {
height: 22px; /* margin-bottom: 2px; */
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
} }
.news-source { .news-source {
height: 22px; /* color: #666; */
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
} }
.news-description { .news-description {
font-family: "微软雅黑";
font-size: 16px; font-size: 16px;
font-weight: 400;
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
line-height: 1.5; line-height: 1.5;
display: -webkit-box; display: -webkit-box;
......
...@@ -16,13 +16,13 @@ defineProps({ ...@@ -16,13 +16,13 @@ defineProps({
}); });
</script> </script>
<style scoped> <style scoped lang="scss">
.custom-title { .custom-title {
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%; width: 100%;
margin-bottom: 20px; margin-bottom: 36px;
padding: 10px 15px; padding: 0 15px;
} }
.color-block { .color-block {
...@@ -41,8 +41,10 @@ defineProps({ ...@@ -41,8 +41,10 @@ defineProps({
} }
.title-text { .title-text {
color: rgba(10, 18, 30, 1);
font-size: 32px; font-size: 32px;
font-weight: 700; font-family: $base-font-family;
font-weight: bold;
margin-left: 20px; margin-left: 20px;
white-space: nowrap; white-space: nowrap;
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
No preview for this file type
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论