提交 50fc6e7d authored 作者: coderBryanFu's avatar coderBryanFu

样式更新

上级 11282794
......@@ -22,7 +22,7 @@ const thinktankRoutes = [
}
},
{
path: "/report/:id",
path: "/thinkTank/report/:id",
name: "ReportDetail",
component: ReportDetail,
meta: {
......
......@@ -514,22 +514,21 @@ onMounted(() => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
......
......@@ -56,7 +56,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer">
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">3.8T</div>
<div class="item-footer">数据量</div>
......@@ -77,7 +77,7 @@
<div class="item-top">285</div>
<div class="item-footer">分析报告</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......@@ -1601,6 +1601,7 @@ onUnmounted(() => {});
.box1 {
width: 1064px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: #fff;
box-sizing: border-box;
......@@ -1914,6 +1915,7 @@ onUnmounted(() => {});
.box2 {
width: 520px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
......@@ -2079,6 +2081,7 @@ onUnmounted(() => {});
.box3 {
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);
.box3-header {
......@@ -2101,14 +2104,14 @@ onUnmounted(() => {});
}
}
.box3-header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 19px;
height: 22px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
.more {
width: 49px;
......@@ -2197,6 +2200,7 @@ onUnmounted(() => {});
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);
.box4-header {
......@@ -2217,14 +2221,14 @@ onUnmounted(() => {});
}
}
.header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 18px;
height: 22px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
.more {
width: 49px;
......@@ -2308,6 +2312,7 @@ onUnmounted(() => {});
.box5 {
width: 1059px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.box5-header {
......@@ -2316,7 +2321,7 @@ onUnmounted(() => {});
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
padding: 0 27px 0 22px;
.box5-header-left {
display: flex;
.box5-header-icon {
......@@ -2329,14 +2334,14 @@ onUnmounted(() => {});
}
}
.box5-header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 19px;
height: 22px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
}
.box5-header-right {
......@@ -2371,6 +2376,7 @@ onUnmounted(() => {});
margin-left: 20px;
width: 521px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1);
.box6-header {
......@@ -2390,14 +2396,14 @@ onUnmounted(() => {});
}
}
.header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 18px;
height: 22px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
.box6-header-right {
margin-left: 130px;
......@@ -2448,7 +2454,7 @@ onUnmounted(() => {});
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
padding: 0 27px 0 22px;
.box7-header-left {
display: flex;
.box7-header-icon {
......@@ -2461,14 +2467,14 @@ onUnmounted(() => {});
}
}
.box7-header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 19px;
height: 22px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
}
.box7-header-right {
......@@ -2520,7 +2526,7 @@ onUnmounted(() => {});
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
padding: 0 27px 0 22px;
.box8-header-left {
display: flex;
.box8-header-icon {
......@@ -2533,14 +2539,14 @@ onUnmounted(() => {});
}
}
.box8-header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 19px;
height: 22px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
}
.box8-header-right {
......@@ -2673,7 +2679,7 @@ onUnmounted(() => {});
margin: 0 auto;
display: flex;
justify-content: space-between;
padding: 0 20px;
padding: 0 27px 0 22px;
.box9-header-left {
display: flex;
.box9-header-icon {
......@@ -2686,14 +2692,14 @@ onUnmounted(() => {});
}
}
.box9-header-title {
margin-top: 16px;
margin-top: 11px;
margin-left: 19px;
height: 22px;
height: 26px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 22px;
line-height: 26px;
}
}
.box9-header-right {
......
......@@ -589,22 +589,21 @@ onMounted(async () => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-switch-box {
position: absolute;
......
......@@ -742,22 +742,21 @@ const handleClickDetail = (isShow) => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-right {
position: absolute;
......
......@@ -279,26 +279,27 @@ onMounted(() => {});
.sider-btn {
margin-top: 20px;
margin-left: 23px;
width: 112px;
width: 120px;
height: 32px;
display: flex;
line-height: 32px;
border-radius: 16px;
cursor: pointer;
.btn-text {
width: 82px;
width: 90px;
height: 32px;
line-height: 32px;
text-align: left;
box-sizing: border-box;
padding-left: 24px;
font-size: 14px;
font-size: 16px;
font-weight: 400;
font-family: Microsoft YaHei;
color: rgba(95, 101, 108, 1);
}
.btn-icon {
width: 22px;
padding-top: 2px;
padding-top: 4px;
}
}
.siderBtnActive {
......
......@@ -722,22 +722,21 @@ onMounted(async () => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-right {
position: absolute;
......
......@@ -634,22 +634,21 @@ onMounted(() => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-top: 14px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-size: 20px;
font-weight: 700;
line-height: 16px;
line-height: 26px;
}
.header-btn-box {
position: absolute;
......
......@@ -603,21 +603,21 @@ onMounted(() => {
display: flex;
position: relative;
.icon {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 16px;
margin-top: 16px;
height: 24px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-size: 20px;
font-weight: 700;
line-height: 24px;
line-height: 26px;
}
.header-right {
display: flex;
......
......@@ -446,21 +446,21 @@ onMounted(async () => {
position: relative;
height: 50px;
.box-header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.box-header-title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
......
......@@ -21,7 +21,7 @@
搜索
</div>
</div>
<div class="search-center">
<!-- <div class="search-center">
<div class="search-item">
<div class="search-item-num">32</div>
<div class="search-item-name">相关法案</div>
......@@ -34,7 +34,7 @@
<div class="search-item-num">41</div>
<div class="search-item-name">相关政府公告</div>
</div>
</div>
</div> -->
<div class="search-bottom">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">最新动态</div>
......@@ -160,7 +160,7 @@ const handleBackHome = () => {
padding: 44px 160px 30px 160px;
.search {
width: 960px;
height: 225px;
height: 168px;
margin: 0 auto 68px auto;
.search-main {
......@@ -251,6 +251,7 @@ const handleBackHome = () => {
width: 688px;
height: 48px;
margin: 0 auto;
margin-top: 36px;
display: flex;
justify-content: space-between;
// gap: 16px;
......
......@@ -55,7 +55,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer" v-show="!isShow">
<!-- <div class="home-main-header-footer" v-show="!isShow">
<div class="home-main-header-footer-item">
<div class="item-top">3.8T</div>
<div class="item-footer">数据量</div>
......@@ -76,7 +76,7 @@
<div class="item-top">285</div>
<div class="item-footer">分析报告</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box" v-show="!isShow">
<div class="btn" @click="handleToPosi('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......
......@@ -280,22 +280,21 @@ const box2LeftActiveIndex = ref(0);
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
......
......@@ -316,21 +316,21 @@ onMounted(() => {
height: 48px;
position: relative;
.icon {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
background: var(--color-main-active);
}
.title {
height: 24px;
height: 26px;
margin-left: 14px;
margin-top: 16px;
color: rgba(10, 87, 166, 1);
margin-top: 14px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 18px;
font-size: 20px;
font-weight: 700;
line-height: 24px;
line-height: 26px;
}
.header-right {
position: absolute;
......
......@@ -205,22 +205,21 @@ const laws = ref([
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
......
......@@ -248,22 +248,21 @@ const eventList = ref([
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 16px;
}
.header-btn-box {
position: absolute;
......
......@@ -25,7 +25,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer">
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">1096</div>
<div class="item-footer">实体清单</div>
......@@ -38,12 +38,6 @@
<div class="item-top">162</div>
<div class="item-footer">关键和新型技术清单</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-btn-box">
<div class="btn" @click="scrollToTop('position1')">
......@@ -468,15 +462,15 @@
header-row-class-name="table-header"
row-class-name="table-row"
>
<el-table-column prop="index" label="序号" width="80" align="center">
<!-- <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> -->
<el-table-column prop="name" label="实体名称" min-width="200">
<template #default="scope">
<div style="font-weight: 500">{{ scope.row.name }}</div>
<div class="tableName">{{ scope.row.name }}</div>
</template>
</el-table-column>
......@@ -1853,6 +1847,7 @@ onMounted(async () => {
font-weight: 400;
line-height: 48px;
margin-left: 36px;
text-align: center;
}
.btn-icon {
position: absolute;
......@@ -1860,7 +1855,7 @@ onMounted(async () => {
right: 19px;
width: 6px;
height: 12px;
img{
img {
width: 100%;
height: 100%;
}
......@@ -2388,6 +2383,15 @@ onMounted(async () => {
}
}
}
.tableName {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
:deep(.el-input__wrapper) {
box-shadow: none;
......
......@@ -25,7 +25,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer">
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">142</div>
<div class="item-footer">实体清单</div>
......@@ -38,7 +38,7 @@
<div class="item-top">326</div>
<div class="item-footer">关键和新型技术清单</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......
......@@ -55,7 +55,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer" v-show="!isShow">
<!-- <div class="home-main-header-footer" v-show="!isShow">
<div class="home-main-header-footer-item">
<div class="item-top">142</div>
<div class="item-footer">创新主体数量</div>
......@@ -76,7 +76,7 @@
<div class="item-top">84</div>
<div class="item-footer">重要技术进展数量</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box" v-show="!isShow">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......
......@@ -20,7 +20,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer">
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">142</div>
<div class="item-footer">总调查案件数</div>
......@@ -41,7 +41,7 @@
<div class="item-top">285</div>
<div class="item-footer">分析报告</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......
......@@ -271,22 +271,22 @@ onMounted(() => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
......
......@@ -542,15 +542,14 @@ onMounted(() => {
.header-left {
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 16px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 20px;
......
......@@ -313,22 +313,21 @@ onMounted(() => {
display: flex;
position: relative;
.header-left {
margin-top: 20px;
margin-top: 18px;
width: 8px;
height: 16px;
height: 20px;
border-radius: 0 4px 4px 0;
background: rgba(10, 87, 166, 1);
}
.title {
margin-left: 22px;
margin-top: 20px;
height: 16px;
line-height: 16px;
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: rgba(10, 87, 166, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 600;
line-height: 16px;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
......
......@@ -21,7 +21,7 @@
搜索
</div>
</div>
<div class="search-center">
<!-- <div class="search-center">
<div class="search-item">
<div class="search-item-num">190</div>
<div class="search-item-name">规则限制政令</div>
......@@ -38,7 +38,7 @@
<div class="search-item-num">12</div>
<div class="search-item-name">排华国家数量</div>
</div>
</div>
</div> -->
<div class="search-bottom">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">最新动态</div>
......@@ -165,7 +165,7 @@ const handleBackHome = () => {
padding: 44px 160px 30px 160px;
.search {
width: 960px;
height: 225px;
height: 168px;
margin: 0 auto 68px auto;
.search-main {
display: flex;
......@@ -255,6 +255,7 @@ const handleBackHome = () => {
width: 688px;
height: 48px;
margin: 0 auto;
margin-top: 36px;
display: flex;
justify-content: space-between;
// gap: 16px;
......
......@@ -21,7 +21,7 @@
搜索
</div>
</div>
<div class="search-center">
<!-- <div class="search-center">
<div class="search-item">
<div class="search-item-num">18</div>
<div class="search-item-name">科研资助机构</div>
......@@ -38,7 +38,7 @@
<div class="search-item-num">15,556</div>
<div class="search-item-name">经费总额(亿美元)</div>
</div>
</div>
</div> -->
<div class="search-bottom">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">最新动态</div>
......@@ -239,7 +239,7 @@ const dataList = ref([
padding: 44px 160px 30px 160px;
.search {
width: 960px;
height: 225px;
height: 168px;
margin: 0 auto 52px auto;
.search-main {
display: flex;
......@@ -329,6 +329,7 @@ const dataList = ref([
width: 688px;
height: 48px;
margin: 0 auto;
margin-top: 36px;
display: flex;
justify-content: space-between;
// gap: 16px;
......
<template>
<div class="effect-analysis">
<div class="placeholder-card">
<div class="card-header">
<CardTitle title="影响分析" />
<el-icon class="expand-icon"><TrendCharts /></el-icon>
</div>
<div class="card-body">
<div class="placeholder-content">
<h3 class="placeholder-title">影响分析功能开发中</h3>
<el-empty description="暂无数据" />
</div>
</div>
</div>
</div>
</template>
<script setup>
import { TrendCharts } from '@element-plus/icons-vue'
import CardTitle from '@/components/CardTitle.vue'
</script>
<style scoped>
.effect-analysis {
/* padding: 20px 0; */
}
.placeholder-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
min-height: 500px;
}
.card-header {
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-body {
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.expand-icon {
color: #999;
}
.placeholder-content {
display: flex;
flex-direction: column;
align-items: center;
max-width: 400px;
}
.placeholder-icon {
margin-bottom: 24px;
color: #d3d3d3;
}
.placeholder-title {
font-size: 20px;
color: #333;
margin: 0 0 16px 0;
}
.placeholder-description {
font-size: 14px;
color: #666;
margin-bottom: 20px;
line-height: 1.6;
}
.feature-list {
list-style: none;
padding: 0;
margin: 0 0 30px 0;
text-align: left;
}
.feature-list li {
padding: 8px 0;
color: #666;
font-size: 14px;
position: relative;
padding-left: 20px;
}
.feature-list li::before {
content: "•";
color: #409eff;
font-weight: bold;
position: absolute;
left: 0;
}
</style>
<template>
<div class="overview-container">
<el-row :gutter="20">
<!-- 左侧内容 -->
<el-col :span="8">
<!-- 内容摘要 -->
<div class="content-card">
<div class="card-header">
<CardTitle title="内容摘要" />
<el-icon class="expand-icon"><ArrowRight /></el-icon>
</div>
<div class="card-body">
<div class="summary-content">
<div v-html="props.reportSummary.summary"></div>
<el-button type="text" class="more-btn">查看更多 ></el-button>
</div>
</div>
</div>
<!-- 涉及科技领域 -->
<div class="content-card tech-fields" style="margin-top: 20px;">
<div class="card-header">
<CardTitle title="涉及科技领域" />
<el-icon class="expand-icon"><ArrowRight /></el-icon>
</div>
<div class="card-body">
<div class="tech-tags">
<el-tag type="primary" size="small" v-for="tag in props.reportSummary.researchTypes" :key="tag">{{ tag }}</el-tag>
</div>
<WordCloud :words="props.reportSummary.researchTypes" />
</div>
</div>
</el-col>
<!-- 右侧主要内容 -->
<el-col :span="16">
<div class="content-card main-content-card">
<div class="card-header">
<CardTitle title="主要内容" />
<div class="content-actions">
<el-button type="plain" size="small">核心发现</el-button>
<el-button type="plain" size="small">政策建议</el-button>
<el-icon class="expand-icon"><Paperclip /></el-icon>
</div>
</div>
<div class="card-body">
<div class="main-content-list">
<div v-for="(item, index) in mainContentList" :key="item.id" class="content-item">
<!-- 左侧:序号 -->
<div class="item-number">{{ item.serialNum }}</div>
<!-- 中间:标题和内容 -->
<div class="item-main">
<h4 class="item-title">{{ item.content }}</h4>
<p class="item-content">{{ item.econtent }}</p>
</div>
<!-- 右侧:标签 -->
<div class="item-tags">
<el-tag
v-for="tag in item.tags"
:key="tag.name"
:type="tag.type"
size="small"
class="content-tag"
>
{{ tag.name }}
</el-tag>
</div>
</div>
</div>
<!-- 分页 -->
<div class="pagination-wrapper">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="totalElements"
layout="prev, pager, next"
:pager-count="5"
small
/>
<span class="total-info">{{ totalElements }}项结果</span>
</div>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { Paperclip } from '@element-plus/icons-vue'
import CardTitle from '@/components/CardTitle.vue'
import { mockMainContentList } from '../mockData'
import WordCloud from './WordCloud.vue'
const props = defineProps({
reportSummary: {
type: Object,
required: true
},
content: {
type: Object,
required: true
}
})
const currentPage = ref(1)
const totalElements = ref(6)
const pageSize = ref(10)
// 标签类型映射 - 根据限制方式设置不同的标签颜色
const getTagType = (tagName) => {
const tagTypeMap = {
'出口管制': 'danger',
'资本管制': 'warning',
'技术封锁': 'danger',
'金融制裁': 'danger',
'对台军售': 'warning',
'关税贸易': 'primary',
'供应链打击': 'warning'
}
return tagTypeMap[tagName] || 'info'
}
// 处理接口数据的方法
const processApiData = (apiResponse) => {
if (!apiResponse || !apiResponse.content) return []
return apiResponse.content.map(item => ({
id: item.id,
title: item.content, // 使用content作为标题
content: item.content, // 内容
econtent: item.econtent,
serialNum: item.serialNum,
tags: item.xzfsList.map(tag => ({
name: tag,
type: getTagType(tag)
}))
}))
}
// 更新分页信息的方法
const updatePagination = (apiResponse) => {
if (apiResponse) {
totalElements.value = apiResponse.totalElements || 0
pageSize.value = apiResponse.size || 10
currentPage.value = (apiResponse.number || 0) + 1 // API返回的是0基础的页码
}
}
const mainContentList = ref(mockMainContentList)
</script>
<style scoped>
.overview-container {
padding: 0;
}
.content-card {
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.card-header {
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.card-body {
padding: 20px;
}
.expand-icon {
color: #999;
cursor: pointer;
margin-left: 10px;
}
.summary-content {
line-height: 1.8;
color: #666;
}
.summary-content p {
margin-bottom: 16px;
text-align: justify;
}
.more-btn {
color: #409eff;
padding: 0;
}
.tech-tags {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 10px;
}
.tag-row {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.tech-tag {
background: #f5f7fa;
border: 1px solid #dcdfe6;
color: #606266;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
}
.tech-tag.ai-chip {
background: #fff7e6;
border-color: #ffd591;
color: #fa8c16;
}
.content-actions {
display: flex;
align-items: center;
}
.main-content-list {
margin-bottom: 20px;
}
.content-item {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #f0f0f0;
gap: 16px;
}
.content-item:last-child {
border-bottom: none;
margin-bottom: 0;
padding-bottom: 0;
}
.item-number {
width: 28px;
height: 28px;
background: #409eff;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 600;
flex-shrink: 0;
margin-top: 2px;
}
.item-main {
flex: 1;
min-width: 0;
}
.item-title {
margin: 0 0 8px 0;
font-size: 16px;
font-weight: 600;
color: #1f2937;
line-height: 1.4;
}
.item-content {
margin: 0;
line-height: 1.6;
color: #4b5563;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-tags {
display: flex;
flex-direction: column;
gap: 6px;
flex-shrink: 0;
min-width: 120px;
align-items: flex-end;
}
.content-tag {
font-size: 12px;
height: 22px;
line-height: 20px;
padding: 0 8px;
white-space: nowrap;
}
.pagination-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding-top: 20px;
border-top: 1px solid #f0f0f0;
}
.total-info {
font-size: 12px;
color: #999;
}
.pagination-wrapper :deep(.el-pagination) {
--el-pagination-font-size: 12px;
}
.pagination-wrapper :deep(.el-pagination .el-pager li) {
min-width: 28px;
height: 28px;
line-height: 28px;
}
.pagination-wrapper :deep(.el-pagination .btn-prev),
.pagination-wrapper :deep(.el-pagination .btn-next) {
height: 28px;
line-height: 28px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.content-item {
flex-direction: column;
gap: 12px;
margin-bottom: 20px;
padding-bottom: 20px;
}
.item-number {
align-self: flex-start;
}
.item-main {
order: 2;
}
.item-tags {
order: 3;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
min-width: auto;
}
.item-title {
font-size: 15px;
}
.item-content {
font-size: 13px;
}
}
</style>
<template>
<div class="policy-tracking">
<el-row :gutter="20">
<!-- 左侧:政策建议落实情况 -->
<el-col :span="14">
<div class="policy-card">
<div class="card-header">
<CardTitle title="政策建议落实情况" />
<div class="filter-buttons">
<el-button
:class="{ active: activeFilter === 'all' }"
@click="setFilter('all')"
size="small"
text
>
全部
</el-button>
<el-button
:class="{ active: activeFilter === 'implemented' }"
@click="setFilter('implemented')"
size="small"
text
>
已实施
</el-button>
<el-button
:class="{ active: activeFilter === 'partial' }"
@click="setFilter('partial')"
size="small"
text
>
部分实施
</el-button>
<el-button
:class="{ active: activeFilter === 'pending' }"
@click="setFilter('pending')"
size="small"
text
>
未实施
</el-button>
</div>
</div>
<div class="card-body">
<div class="policy-list">
<div
v-for="(item, index) in policyList"
:key="index"
class="policy-item"
@click="openPolicyDetail(item)"
>
<div class="policy-number">{{ index + 1 }}</div>
<div class="policy-content">
<p class="policy-text">{{ item.text }}</p>
<div class="policy-tags">
<el-tag size="small" v-for="tag in item.tags" :key="tag">{{ tag }}</el-tag>
</div>
<div class="policy-related-bill" v-if="getRelatedBills(item).length > 0">
<div
v-for="(bill, billIndex) in getRelatedBills(item)"
:key="billIndex"
class="bill-item"
>
<span class="bill-tag">法案</span>
<span class="bill-year">{{ bill.year }}</span>
<span class="bill-title">{{ bill.title }}</span>
<el-icon class="bill-arrow"><ArrowRight /></el-icon>
</div>
</div>
</div>
<div class="policy-status">
<el-tag :type="getStatusType(item.status)" size="small">{{ item.status }}</el-tag>
</div>
</div>
</div>
</div>
</div>
</el-col>
<!-- 右侧:相关政策动态 -->
<el-col :span="10">
<div class="policy-dynamics-card">
<div class="card-header">
<CardTitle title="相关政策动态" />
<el-button type="text" size="small" class="more-link">
查看更多
<el-icon><ArrowRight /></el-icon>
</el-button>
</div>
<div class="card-body">
<!-- 暂未开发 -->
<!-- <el-empty description="暂无数据" /> -->
<div class="dynamics-list">
<div
v-for="(item, index) in policyDynamics"
:key="index"
class="dynamics-item"
>
<div class="dynamics-content">
<div class="dynamics-header">
<div class="dynamics-dot"></div>
<h4 class="dynamics-title">{{ item.title }}</h4>
<span class="dynamics-date">{{ item.date }}</span>
</div>
<p class="dynamics-description">{{ item.description }}</p>
<div class="dynamics-image" v-if="item.image">
<img :src="item.image" :alt="item.title" />
</div>
</div>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
<!-- 政策详情弹窗 -->
<el-dialog
v-model="showDetailModal"
title="智库政策追踪"
width="800px"
class="policy-detail-dialog"
@close="closeDetailModal"
>
<div v-if="selectedPolicy" class="policy-detail-content">
<!-- 政策标题 -->
<h3 class="policy-detail-title">{{ selectedPolicy.text }}</h3>
<!-- 标签区域 -->
<div class="policy-detail-tags">
<el-tag size="small">{{ selectedPolicy.category }}</el-tag>
<el-tag size="small">{{ selectedPolicy.subcategory }}</el-tag>
</div>
<!-- 政策详细内容 -->
<div class="policy-detail-body">
<p class="policy-description">
{{ selectedPolicy.detailedDescription || selectedPolicy.text }}
</p>
</div>
<!-- 相关法案信息 -->
<div v-if="selectedPolicy.relatedBill" class="related-bill-section">
<div class="bill-info">
<span class="bill-label">相关立法已通过:</span>
<span class="bill-name">{{ selectedPolicy.relatedBill }}</span>
<el-tag
:type="getStatusType(selectedPolicy.status)"
size="small"
class="bill-status"
>
{{ selectedPolicy.status }}
</el-tag>
</div>
</div>
<!-- 右侧信息区域 -->
<div class="policy-side-info">
<div class="think-tank-info">
<div class="think-tank-cover">
<img src="https://picsum.photos/120/160?random=7" alt="智库报告" />
</div>
<div class="think-tank-details">
<h4 class="think-tank-title">兰德科技智库</h4>
<p class="publish-date">2025年6月26日</p>
<p class="report-title">中美经济竞争:复杂经济和地缘政治关系中的收益与风险</p>
<el-button type="primary" size="small" class="download-btn">
<el-icon><Download /></el-icon>
下载智库报告原文
</el-button>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { ArrowRight, Download } from '@element-plus/icons-vue'
import CardTitle from '@/components/CardTitle.vue'
import PolicyOverview from '../components/PolicyOverview.vue'
// props
const props = defineProps({
policy: {
type: Array,
required: true
}
})
const activeFilter = ref('all')
const showDetailModal = ref(false)
const selectedPolicy = ref(null)
// 政策建议落实情况数据
const policyList = ref([
{
text: '允许OPT的国际学生出国旅行并持多次入境签证重新进入美国。',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [
{ year: '2024', title: '重塑美国人口结构法案' },
{ year: '2025', title: '开放人才法案' }
],
status: '已实施',
detailedDescription: '当前基于就业的绿卡上限设定为140,000个。每个国家的上限设定为总数的7%。这意味着每年只有大约9,800名在美国的中国工人可以通过就业优先类别成为合法永久居民。这个上限造成了符合要求的中国申请者的积压,他们必须等待多年才能获得绿卡批准。在这种情况中,高技能移民报告他们有意愿离开第三国或返回原籍国。美国可以从自己1992年通过的《华人学生保护法》中汲取训练。该法案使某些中国公民免于上限和其他要求。因此,54,000名中国移民住于了这个机会。受过教育的中国移民的回归率较低于今天,尽管有美国移民身份可以让他们提高工人在中国劳动力市场的价值并降低回中国的风险来同时间注,但持有绿卡可以对回归的效果太大,因为维持绿卡需要满足国际任务要求。'
},
{
text: '增加中国公民可获得 H-1B 签证数量。',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [
{ year: '2024', title: '调整签证政策以吸纳外国重点领域人才法案' }
],
status: '部分实施'
},
{
text: '通过就业偏好类别增加卡的数量。',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [
{ year: '2024', title: '重塑美国人口结构法案' }
],
status: '已实施'
},
{
text: '推动清洁能源生产的内,化石燃料重新配置出口。',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [],
status: '未实施'
},
{
text: '支持国内核工业商业化扩张。',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [
{ year: '2024', title: '重塑美国人口结构法案' }
],
status: '已实施'
},
{
text: '在美国和友好国家建立关键矿产加工能力',
tags: ['人才交流', '移民政策'],
subcategory: '移民政策',
relatedBills: [
{ year: '2024', title: '重塑美国人口结构法案' },
{ year: '2025', title: '开放人才法案' }
],
status: '已实施'
}
])
// 获取相关法案列表(兼容旧数据格式)
const getRelatedBills = (item) => {
if (item.relatedBills && Array.isArray(item.relatedBills)) {
return item.relatedBills
}
// 兼容旧格式:relatedBill 字符串
if (item.relatedBill) {
const match = item.relatedBill.match(/^(\d{4})(.+)$/)
if (match) {
return [{ year: match[1], title: match[2] }]
}
}
return []
}
// 相关政策动态数据
const policyDynamics = ref([
{
title: '减税与就业法案的出台与立法博弈',
date: '2025-08-30',
description: '2017年,美国通过了一项旨在根本性以来最为重大的税制改革——《减税与就业法案》...',
image: 'https://picsum.photos/80/60?random=1'
},
{
title: '信用评级机构穆迪下调美国信用评级',
date: '2025-05-16',
description: '2025年5月16日,信用评级机构穆迪自2011年以来第三次下调美国主权信用评级,首...',
image: 'https://picsum.photos/80/60?random=2'
},
{
title: '马斯克与特朗普首次会',
date: '2025-05-16',
description: '马斯克5月30日高任政府效率部负责人时与特朗普首次会面别,但6月3日美然批评OBB...',
image: 'https://picsum.photos/80/60?random=3'
},
{
title: '马斯克成立"美国党"',
date: '2025-05-16',
description: '7月5日(法案签署次日),马斯克宣布成立新政党"美国党",计划参与2026年中期...',
image: 'https://picsum.photos/80/60?random=4'
},
{
title: '"90天关税暂缓期"即将到期',
date: '2025-05-16',
description: '法案通过议会美国对多国"90天关税暂缓期"即将到期(2025年7月9日),欧盟...',
image: 'https://picsum.photos/80/60?random=5'
},
{
title: '减税与就业法案的出台与立法博弈',
date: '2025-08-30',
description: '2017年,美国通过了一项旨在根本性以来最为重大的税制改革——《减税与就业法案》...',
image: 'https://picsum.photos/80/60?random=6'
}
])
// 过滤后的政策列表
const filteredPolicyList = computed(() => {
if (activeFilter.value === 'all') {
return policyList.value
}
const statusMap = {
'implemented': '已实施',
'partial': '部分实施',
'pending': '未实施'
}
return policyList.value.filter(item => item.status === statusMap[activeFilter.value])
})
// 设置过滤器
const setFilter = (filter) => {
activeFilter.value = filter
}
// 获取状态标签类型
const getStatusType = (status) => {
const typeMap = {
'已实施': 'success',
'部分实施': 'warning',
'未实施': 'info'
}
return typeMap[status] || 'info'
}
// 打开政策详情弹窗
const openPolicyDetail = (policy) => {
selectedPolicy.value = policy
showDetailModal.value = true
}
// 关闭政策详情弹窗
const closeDetailModal = () => {
showDetailModal.value = false
selectedPolicy.value = null
}
</script>
<style scoped>
.policy-tracking {
padding: 0;
}
.policy-card,
.policy-dynamics-card {
border: 1px solid #e4e7ed;
border-radius: 8px;
height: fit-content;
}
.policy-card,
.policy-dynamics-card {
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.policy-card .card-header,
.policy-dynamics-card .card-header {
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.policy-card .card-body,
.policy-dynamics-card .card-body {
padding: 20px;
max-height: 600px;
overflow-y: auto;
}
.filter-buttons {
display: flex;
gap: 8px;
}
.filter-buttons .el-button {
color: #666;
padding: 4px 8px;
border-radius: 4px;
border: 1px solid transparent;
}
.filter-buttons .el-button.active {
color: #409eff;
background: #ecf5ff;
border-color: #b3d8ff;
}
.more-link {
color: #666;
display: flex;
align-items: center;
gap: 4px;
}
/* 左侧政策列表样式 */
.policy-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.policy-item {
display: flex;
gap: 12px;
padding-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.2s;
}
.policy-item:hover {
background-color: #f8f9fa;
}
.policy-item:last-child {
border-bottom: none;
padding-bottom: 0;
}
.policy-item:last-child:hover {
padding-bottom: 12px;
}
.policy-number {
width: 24px;
height: 24px;
background: #409eff;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
flex-shrink: 0;
margin-top: 2px;
}
.policy-content {
flex: 1;
}
.policy-text {
margin: 0 0 12px 0;
line-height: 1.6;
color: #333;
font-size: 14px;
}
.policy-tags {
display: flex;
gap: 8px;
margin-bottom: 12px;
}
.policy-related-bill {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-top: 8px;
}
.bill-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
background: #fff;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.bill-item:hover {
background: #f5f7fa;
}
.bill-tag {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 8px;
background: #a8d5ff;
color: #fff;
font-size: 12px;
font-weight: 500;
border-radius: 3px;
white-space: nowrap;
}
.bill-year {
font-size: 13px;
font-weight: 500;
color: #1e3a8a;
white-space: nowrap;
}
.bill-title {
font-size: 13px;
font-weight: 500;
color: #1e3a8a;
white-space: nowrap;
}
.bill-arrow {
width: 18px;
height: 18px;
background: #a8d5ff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #1e3a8a;
font-size: 12px;
flex-shrink: 0;
}
.policy-status {
/* background: #f8f9fa; */
padding: 8px 12px;
border-radius: 4px;
/* border-left: 3px solid #409eff; */
}
.status-info {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.status-label {
font-size: 12px;
color: #666;
}
.status-bill {
font-size: 12px;
color: #409eff;
font-weight: 500;
}
.status-tag {
margin-left: auto;
}
/* 右侧动态列表样式 */
.dynamics-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.dynamics-item {
position: relative;
padding-left: 16px;
}
.dynamics-item:not(:last-child)::before {
content: '';
position: absolute;
left: 6px;
top: 20px;
bottom: -16px;
width: 1px;
background: #e4e7ed;
}
.dynamics-content {
background: white;
position: relative;
}
.dynamics-header {
display: flex;
align-items: flex-start;
gap: 8px;
margin-bottom: 8px;
position: relative;
}
.dynamics-dot {
width: 8px;
height: 8px;
background: #409eff;
border-radius: 50%;
flex-shrink: 0;
margin-top: 6px;
position: absolute;
left: -12px;
z-index: 1;
}
.dynamics-title {
font-size: 14px;
font-weight: 600;
color: #409eff;
margin: 0;
flex: 1;
line-height: 1.4;
}
.dynamics-date {
font-size: 12px;
color: #999;
flex-shrink: 0;
}
.dynamics-description {
font-size: 13px;
color: #666;
line-height: 1.5;
margin: 0 0 8px 0;
max-width: 320px;
}
.dynamics-image {
width: 80px;
height: 60px;
border-radius: 4px;
overflow: hidden;
float: right;
margin-left: 12px;
margin-top: -40px;
}
.dynamics-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 弹窗样式 */
.policy-detail-dialog :deep(.el-dialog) {
border-radius: 8px;
}
.policy-detail-dialog :deep(.el-dialog__header) {
padding: 20px 24px 16px;
border-bottom: 1px solid #e4e7ed;
}
.policy-detail-dialog :deep(.el-dialog__title) {
font-size: 18px;
font-weight: 600;
color: #333;
}
.policy-detail-dialog :deep(.el-dialog__body) {
padding: 24px;
}
.policy-detail-content {
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
.policy-detail-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin: 0;
line-height: 1.5;
padding-right: 140px;
}
.policy-detail-tags {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.policy-detail-body {
margin: 16px 0;
}
.policy-description {
font-size: 14px;
color: #666;
line-height: 1.8;
margin: 0;
text-align: justify;
padding-right: 140px;
}
.related-bill-section {
background: #f8f9fa;
padding: 16px;
border-radius: 6px;
border-left: 3px solid #409eff;
margin-right: 140px;
}
.bill-info {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.bill-label {
font-size: 14px;
color: #666;
}
.bill-name {
font-size: 14px;
color: #409eff;
font-weight: 500;
}
.bill-status {
margin-left: auto;
}
/* 右侧信息区域 */
.policy-side-info {
position: absolute;
top: 0;
right: 0;
width: 120px;
}
.think-tank-info {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.think-tank-cover {
width: 120px;
height: 160px;
border-radius: 6px;
overflow: hidden;
margin-bottom: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.think-tank-cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
.think-tank-details {
width: 100%;
}
.think-tank-title {
font-size: 12px;
font-weight: 600;
color: #333;
margin: 0 0 4px 0;
}
.publish-date {
font-size: 11px;
color: #999;
margin: 0 0 8px 0;
}
.report-title {
font-size: 11px;
color: #666;
line-height: 1.4;
margin: 0 0 12px 0;
display: -webkit-box;
-webkit-line-clamp: 3;
line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.download-btn {
font-size: 10px;
padding: 4px 8px;
height: auto;
display: flex;
align-items: center;
gap: 4px;
}
.download-btn .el-icon {
font-size: 12px;
}
/* 响应式调整 */
@media (max-width: 768px) {
.policy-tracking :deep(.el-row) {
flex-direction: column;
}
.policy-tracking :deep(.el-col) {
width: 100% !important;
margin-bottom: 20px;
}
.policy-detail-title,
.policy-description {
padding-right: 0;
}
.related-bill-section {
margin-right: 0;
}
.policy-side-info {
position: static;
width: 100%;
margin-top: 20px;
}
.think-tank-info {
flex-direction: row;
text-align: left;
gap: 16px;
}
.think-tank-cover {
width: 80px;
height: 100px;
margin-bottom: 0;
}
.think-tank-details {
flex: 1;
}
}
</style>
\ No newline at end of file
<template>
<div class="word-cloud-container">
<svg :width="width" :height="height" ref="svgRef">
</svg>
<div v-if="!words || words.length === 0" class="no-data-message">
{{ emptyText }}
</div>
</div>
</template>
<script>
import { defineComponent, ref, watch, onMounted, nextTick } from 'vue';
import * as d3 from 'd3';
import cloud from 'd3-cloud';
// 默认颜色数组,您可以根据需要扩展
const defaultColors = [
'#f56c6c', // 红色
'#e6a23c', // 橙色
'#67c23a', // 绿色
'#409eff', // 蓝色
'#909399', // 灰色
'#b3a2c7', // 紫色
'#49a37e', // 军绿
];
export default defineComponent({
name: 'WordCloud',
props: {
// 词汇数组,例如:['通用人工智能', 'AI芯片', '出口管制']
words: {
type: Array,
required: true,
default: () => [],
},
// 词云容器的宽度
width: {
type: [Number, String],
default: 600,
},
// 词云容器的高度
height: {
type: [Number, String],
default: 400,
},
// 词汇的最小字号
minFontSize: {
type: Number,
default: 16,
},
// 词汇的最大字号
maxFontSize: {
type: Number,
default: 50,
},
// 词汇颜色数组
colors: {
type: Array,
default: () => defaultColors,
},
// 词汇为空时的提示文本
emptyText: {
type: String,
default: '暂无词云数据',
},
},
setup(props) {
const svgRef = ref(null);
/**
* @description 根据词汇数组,计算词频,并转换成d3-cloud所需的格式
* @param {Array<string>} rawWords 原始词汇数组
* @returns {Array<{text: string, size: number}>} 转换后的数据
*/
const prepareData = (rawWords) => {
if (!rawWords || rawWords.length === 0) return [];
// 1. 计算词频
const wordCounts = rawWords.reduce((acc, word) => {
acc[word] = (acc[word] || 0) + 1;
return acc;
}, {});
// 2. 转换为d3-cloud所需的格式,并找出最大/最小词频
let minCount = Infinity;
let maxCount = -Infinity;
const data = Object.entries(wordCounts).map(([text, count]) => {
if (count < minCount) minCount = count;
if (count > maxCount) maxCount = count;
return { text, count };
});
// 3. 将词频映射到字号范围
const sizeScale = d3
.scaleLinear()
.domain([minCount, maxCount])
.range([props.minFontSize, props.maxFontSize]);
return data.map((d) => ({
...d,
size: sizeScale(d.count), // size就是字号
}));
};
/**
* @description 绘制词云
* @param {Array<{text: string, size: number, x: number, y: number, rotate: number}>} words 布局后的词汇数据
*/
const drawCloud = (words) => {
const svg = d3.select(svgRef.value);
// 清空SVG内容
svg.selectAll('*').remove();
// 创建一个<g>元素并平移到中心
const g = svg
.append('g')
.attr(
'transform',
`translate(${props.width / 2}, ${props.height / 2})`
);
// 绑定数据并创建<text>元素
const wordElements = g.selectAll('text').data(words, (d) => d.text);
// 进入(Enter)阶段:添加新的<text>元素
wordElements
.enter()
.append('text')
.style('font-size', (d) => `${d.size}px`)
.style('fill', (_, i) => props.colors[i % props.colors.length]) // 循环使用颜色
.attr('text-anchor', 'middle') // 文本居中
// *********** 关键修改:移除悬浮样式和交互,保持水平布局 ***********
.attr('transform', (d) => `translate(${d.x}, ${d.y})`) // 移除rotate属性
.text((d) => d.text);
// ************************************************************
// 退出(Exit)阶段:移除多余的<text>元素(如果更新时词汇减少)
wordElements.exit().remove();
};
/**
* @description 启动d3-cloud布局引擎
* @param {Array<{text: string, count: number, size: number}>} data 待布局的词汇数据
*/
const generateLayout = (data) => {
if (!data || data.length === 0) {
d3.select(svgRef.value).selectAll('*').remove();
return;
}
const layout = cloud()
.size([props.width, props.height]) // 词云的尺寸
.words(data) // 传入词汇数据
.padding(5) // 词汇之间的最小间距
// *********** 关键修改:固定旋转角度为 0,实现水平布局 ***********
.rotate(0)
// ************************************************************
.font('Impact') // 字体
.fontSize((d) => d.size) // 使用计算出的字号
.on('end', drawCloud); // 布局计算完成后调用drawCloud
layout.start(); // 启动布局
};
/**
* @description 核心渲染函数,处理数据并启动布局
*/
const renderWordCloud = () => {
// 保证在DOM更新后执行
nextTick(() => {
const processedData = prepareData(props.words);
generateLayout(processedData);
});
};
onMounted(renderWordCloud);
// 监听props变化,重新渲染词云
watch(() => [props.words, props.width, props.height, props.minFontSize, props.maxFontSize], () => {
renderWordCloud();
}, { deep: true });
return {
svgRef,
renderWordCloud,
};
},
});
</script>
<style lang="scss">
.word-cloud-container {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: v-bind(width + 'px'); // 动态绑定宽度
height: v-bind(height + 'px'); // 动态绑定高度
border: 1px solid #ebeef5;
border-radius: 4px;
overflow: hidden;
// 词云SVG的样式
svg {
display: block; // 移除底部空白
}
// 词汇文本的通用样式
text {
font-weight: bold;
// *********** 关键修改:移除transition和cursor,避免交互 ***********
}
.no-data-message {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
color: #909399;
background-color: #f5f7fa;
}
}
</style>
\ No newline at end of file
<template>
<div class="page-container">
<!-- Header 占满宽度 -->
<header class="report-header">
<div class="header-container">
<div class="header-content">
<!-- 左侧封面图片 -->
<div class="cover-image-area">
<div class="book-cover">
<div class="cover-title">{{ reportSummary.name }}</div>
<div class="cover-subtitle">{{ reportSummary.ename }}</div>
<div class="cover-globe"></div>
<div class="wrap">
<div class="header">
<div class="header-top">
<div class="header-top-left">
<div class="title">{{ "中美经济竞争:复杂经济和地缘政治关系中的收益和风险" }}</div>
<div class="en-title">
{{ "U.S.-China Economic CompetitionGains and Risks in a Complex Economic and Geopolitical Relationship" }}
</div>
<div class="tag-box">
<div class="tag">{{ "外交" }}</div>
<div class="tag">{{ "军事" }}</div>
<div class="tag">{{ "经济" }}</div>
</div>
<!-- 中间标题区域 -->
<div class="title-area">
<h1 class="main-title">{{ reportSummary.name }}</h1>
<h2 class="sub-title">{{ reportSummary.ename }}</h2>
<div class="tags-area">
<span
v-for="(tag, index) in reportSummary.tags"
:key="tag"
class="tag-item"
:class="{ 'tag-primary': index % 2 === 0, 'tag-danger': index % 2 === 1 }"
>
{{ tag }}
</span>
</div>
<div class="header-top-right">
<div class="name">{{ "兰德科技智库" }}</div>
<div class="time">{{ "2025年6月23日" }}</div>
</div>
<!-- 右侧元信息 -->
<div class="meta-info">
<div class="source-info">
<el-icon class="source-icon"><TrendCharts /></el-icon>
<span class="source-name">{{ reportSummary.thinkTankName }}</span>
</div>
<div class="publish-date">{{ reportSummary.times }}</div>
<div class="header-bottom">
<div class="tab-box">
<div class="tab" :class="{ tabActive: tabActiveName === '报告分析' }" @click="switchTab('报告分析')">
<div class="icon">
<img v-show="tabActiveName === '报告分析'" src="./images/tab-icon-active.png" alt="" />
<img v-show="tabActiveName !== '报告分析'" src="./images/tab-icon.png" alt="" />
</div>
<div class="text" :class="{ textActive: tabActiveName === '报告分析' }">{{ "报告分析" }}</div>
</div>
<div class="tab" :class="{ tabActive: tabActiveName === '政策追踪' }" @click="switchTab('政策追踪')">
<div class="icon">
<img v-show="tabActiveName === '政策追踪'" src="./images/tab-icon2-active.png" alt="" />
<img v-show="tabActiveName !== '政策追踪'" src="./images/tab-icon2.png" alt="" />
</div>
<!-- Tab切换区域 -->
<div class="header-tabs">
<div class="tabs-container">
<el-tabs v-model="activeTab" class="header-tab-nav">
<el-tab-pane label="内容摘要" name="summary">
<template #label>
<span class="tab-label">
<el-icon><Document /></el-icon>
内容摘要
</span>
</template>
</el-tab-pane>
<el-tab-pane label="政策追踪" name="policy">
<template #label>
<span class="tab-label">
<el-icon><Search /></el-icon>
政策追踪
</span>
</template>
</el-tab-pane>
<!-- <el-tab-pane label="影响分析" name="analysis">
<template #label>
<span class="tab-label">
<el-icon><TrendCharts /></el-icon>
影响分析
</span>
</template>
</el-tab-pane> -->
</el-tabs>
<div class="action-buttons">
<!-- <el-button type="plain" size="default">
<el-icon><Link /></el-icon>
查看官网
</el-button> -->
<el-button type="plain" size="default">
<el-icon><Download /></el-icon>
下载原文
</el-button>
<el-button type="primary" size="default">
<el-icon><DataAnalysis /></el-icon>
分析报告
</el-button>
<div class="text" :class="{ textActive: tabActiveName === '政策追踪' }">{{ "政策追踪" }}</div>
</div>
</div>
<div class="btn-box">
<div class="btn">
<div class="icon">
<img src="./images/btn-icon1.png" alt="" />
</div>
<div class="text">{{ "查看官网" }}</div>
</div>
<div class="btn">
<div class="icon">
<img src="./images/btn-icon2.png" alt="" />
</div>
<div class="text">{{ "报告原文" }}</div>
</div>
<div class="btn">
<div class="icon">
<img src="./images/btn-icon3.png" alt="" />
</div>
<div class="text">{{ "文档下载" }}</div>
</div>
<div class="btn btn1">
<div class="icon">
<img src="./images/btn-icon4.png" alt="" />
</div>
<div class="text">{{ "分析报告" }}</div>
</div>
</div>
</header>
<!-- Main Content 最大宽度1200px并居中 -->
<main class="main-content">
<div class="content-container">
<!-- 根据activeTab显示不同的组件 -->
<Overview v-if="activeTab === 'summary'" :reportSummary="reportSummary" :content="content" />
<Policy v-else-if="activeTab === 'policy'" :policy="policy" />
<Effect v-else-if="activeTab === 'analysis'" />
</div>
</main>
</div>
<div class="main">
<ReportAnalysis></ReportAnalysis>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import {
Document,
Search,
TrendCharts,
Link,
Download,
DataAnalysis
} from '@element-plus/icons-vue'
import {
getThinkTankReportSummary,
getThinkTankReportContent,
getThinkTankReportPolicy
} from '@/api'
import { useRoute } from 'vue-router'
import { mockContent } from '../mockData'
import { ref } from "vue";
// 引入组件
import Overview from './Overview.vue'
import Policy from './Policy.vue'
import Effect from './Effect.vue'
import ReportAnalysis from './reportAnalysis/index.vue'
const activeTab = ref('summary')
const route = useRoute()
const reportSummary = ref({})
const content = ref({})
const policy = ref([])
const getReportSummary = async () => {
const { data } = await getThinkTankReportSummary({ id: route.params.id })
console.log('getReportSummary', data)
reportSummary.value = data
reportSummary.value = {
thinkTankName: '兰德公司',
name: '美国经济竞争分析',
ename: 'Analysis of US Economic Competition',
tags: ['美国', '经济竞争', '政治竞争', '军事竞争'],
times: '2025-11-27',
summary: mockContent,
researchTypes: ['人工智能',
'军事与安全',
'半导体与高科技',
'经济与贸易',
'国际规则与多边体系',
'地缘政治'],
}
}
const getContent = async () => {
const { data } = await getThinkTankReportContent({ id: route.params.id })
console.log('getContent', data)
content.value = data
}
const tabActiveName = ref("报告分析");
const getPolicy = async () => {
const { data } = await getThinkTankReportPolicy({ id: route.params.id })
console.log('getPolicy', data)
policy.value = data
}
onMounted(() => {
getReportSummary()
getContent()
getPolicy()
})
const switchTab = name => {
tabActiveName.value = name;
};
</script>
<style scoped>
.page-container {
background-color: #f5f7fa;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
min-height: 100vh;
width: 100%;
--max-width: 1650px;
}
/* Header Styles - 占满宽度 */
.report-header {
background-color: #fff;
width: 100%;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.header-container {
max-width: var(--max-width);
margin: 0 auto;
padding: 32px 24px 0;
}
.header-content {
display: flex;
align-items: flex-start;
gap: 24px;
}
/* 左侧封面图片区域 */
.cover-image-area {
flex-shrink: 0;
}
.book-cover {
width: 88px;
height: 115px;
background: linear-gradient(135deg, #d4a574 0%, #c8965f 100%);
border-radius: 4px;
padding: 16px 12px;
<style lang="scss" scoped>
.wrap {
width: 1920px;
height: 984px;
.header {
width: 1920px;
height: 188px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.header-top {
margin-top: 20px;
margin-left: 248px;
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.cover-title {
font-size: 14px;
font-weight: 600;
color: #fff;
line-height: 1.3;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.cover-subtitle {
font-size: 10px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.2;
margin-top: 4px;
}
.cover-globe {
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 60px;
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.3) 0%, transparent 70%);
border-radius: 50%;
opacity: 0.6;
}
.cover-globe::before {
content: '';
position: absolute;
top: 20%;
left: 30%;
width: 40%;
height: 30%;
background: rgba(255, 255, 255, 0.2);
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
}
.cover-globe::after {
content: '';
position: absolute;
top: 50%;
right: 20%;
width: 35%;
height: 25%;
background: rgba(255, 255, 255, 0.15);
border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
}
/* 中间标题区域 */
.title-area {
flex: 1;
min-width: 0;
}
.main-title {
font-size: 28px;
margin-right: 160px;
.header-top-left {
.title {
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
color: #2c3e50;
margin: 0 0 10px 0;
line-height: 1.4;
}
.sub-title {
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.en-title {
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
color: #7f8c8d;
font-weight: 400;
margin: 0 0 16px 0;
line-height: 1.5;
}
.tags-area {
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.tag-box {
margin-top: 11px;
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.tag-item {
display: inline-block;
padding: 4px 12px;
gap: 8px;
.tag {
height: 26px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
font-size: 13px;
font-weight: 500;
border: 1.5px solid;
background: transparent;
}
.tag-primary {
color: #409eff;
border-color: #409eff;
}
.tag-danger {
color: #f56c6c;
border-color: #f56c6c;
}
/* 右侧元信息 */
.meta-info {
flex-shrink: 0;
background: rgba(246, 250, 255, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
.header-top-right {
.name {
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: right;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 12px;
}
.source-info {
display: flex;
align-items: center;
gap: 6px;
color: #606266;
}
.source-icon {
font-size: 18px;
color: #8B5CF6;
}
.source-name {
font-size: 14px;
color: #606266;
font-weight: 500;
}
.publish-date {
color: #606266;
font-size: 14px;
}
.time {
height: 24px;
margin-top: 5px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
}
/* Header Tabs区域 */
.header-tabs {
background-color: #fff;
width: 100%;
margin-top: 20px;
}
.tabs-container {
max-width: var(--max-width);
line-height: 24px;
letter-spacing: 0px;
text-align: right;
}
}
}
.header-bottom {
margin: 0 auto;
padding: 0 24px;
margin-top: 30px;
width: 1600px;
height: 48px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e4e7ed;
}
.header-tab-nav {
flex: 1;
margin: 0;
}
.header-tab-nav :deep(.el-tabs__header) {
margin: 0;
border-bottom: none;
}
.header-tab-nav :deep(.el-tabs__nav-wrap) {
padding: 0;
}
.header-tab-nav :deep(.el-tabs__nav-wrap::after) {
display: none;
}
.header-tab-nav :deep(.el-tabs__item) {
font-size: 16px;
font-weight: 500;
color: #606266;
padding: 16px 24px;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}
.header-tab-nav :deep(.el-tabs__item:hover) {
color: #409eff;
}
.header-tab-nav :deep(.el-tabs__item.is-active) {
color: #409eff;
border-bottom-color: #409eff;
font-weight: 600;
}
.header-tab-nav :deep(.el-tabs__active-bar) {
display: none;
}
.tab-label {
.tab-box {
width: 224px;
height: 48px;
display: flex;
align-items: center;
gap: 5px;
}
.action-buttons {
display: flex;
align-items: center;
}
.action-buttons .el-button {
gap: 24px;
.tab {
width: 92px;
height: 48px;
display: flex;
align-items: center;
gap: 4px;
}
/* Main Content Styles - 最大宽度1200px并居中 */
.main-content {
width: 100%;
display: flex;
justify-content: center;
padding: 0 24px;
}
.content-container {
max-width: var(--max-width);
gap: 4px;
cursor: pointer;
border-bottom: 2px solid transparent;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
border-radius: 8px;
}
/* 响应式设计 */
@media (max-width: 1240px) {
.header-container {
padding: 24px 20px 0;
}
.tabs-container {
padding: 0 20px;
height: 100%;
}
}
@media (max-width: 1040px) {
.content-container {
margin: 0 20px;
}
.main-content {
padding: 0;
}
}
@media (max-width: 768px) {
.header-container {
padding: 20px 16px 0;
}
.header-content {
flex-direction: column;
gap: 20px;
}
.cover-image-area {
align-self: center;
}
.book-cover {
width: 100px;
height: 140px;
.text {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 400;
line-height: 24px;
}
.title-area {
text-align: center;
.textActive {
color: rgba(5, 95, 194, 1);
font-weight: 700;
}
.main-title {
font-size: 22px;
}
.sub-title {
font-size: 14px;
.tabActive {
border-bottom: 2px solid rgba(5, 95, 194, 1);
}
.tags-area {
justify-content: center;
}
.meta-info {
.btn-box {
display: flex;
gap: 12px;
.btn {
width: 120px;
height: 36px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 6px;
background: rgba(255, 255, 255, 1);
display: flex;
align-items: center;
text-align: center;
}
.tabs-container {
flex-direction: column;
gap: 16px;
align-items: stretch;
padding: 0 16px;
}
.header-tab-nav :deep(.el-tabs__item) {
padding: 12px 16px;
font-size: 14px;
}
.action-buttons {
justify-content: center;
gap: 8px;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
.content-container {
margin: 0 16px;
padding: 16px;
}
}
@media (max-width: 480px) {
.header-container {
padding: 16px 12px 0;
}
.book-cover {
width: 80px;
height: 120px;
padding: 12px 8px;
}
.cover-title {
font-size: 11px;
}
.cover-subtitle {
font-size: 8px;
}
.cover-globe {
width: 45px;
height: 45px;
bottom: 15px;
}
.main-title {
font-size: 20px;
}
.sub-title {
font-size: 13px;
}
.tag-item {
font-size: 12px;
padding: 3px 10px;
}
.source-icon {
.text {
width: 64px;
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: center;
}
.source-name {
font-size: 13px;
}
.publish-date {
font-size: 13px;
.btn1 {
border-radius: 6px;
background: var(--color-main-active);
.text{
color: rgba(255,255,255,1);
}
.tabs-container {
padding: 0 12px;
}
.header-tab-nav :deep(.el-tabs__item) {
padding: 10px 12px;
font-size: 13px;
}
.content-container {
margin: 0 12px;
padding: 12px;
}
.action-buttons {
flex-wrap: wrap;
gap: 8px;
}
.action-buttons .el-button {
font-size: 12px;
.main{
width: 100%;
}
}
</style>
\ No newline at end of file
<template>
<div class="wrap">
<div class="left">
<div class="box1">
<div class="box-header">
<div class="header-left"></div>
<div class="title">内容摘要</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box1-main">
{{
`包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。
为了应对这一挑战,兰德大学的研究人员对美中竞争进行了经济和制度分析,进行了参与式的远见练习,以了解确保美国经济健康的长期路径,并创建了两个经济竞争游戏,探索多个国家在相互交流的同时确保经济健康的动态...`
}}
</div>
<div class="box1-footer">
<div class="text">{{ "查看更多" }}</div>
<div class="icon">
<img src="@/assets/images/icon-double-down.png" alt="" />
</div>
</div>
</div>
<div class="box2">
<div class="box-header">
<div class="header-left"></div>
<div class="title">涉及科技领域</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box2-main">
<div class="box2-main-tag-box">
<div class="tag" v-for="(item, index) in areaList" :key="index">{{ item }}</div>
</div>
<div class="box2-content" id="box2Chart"></div>
</div>
</div>
</div>
<div class="right">
<div class="box3">
<div class="box-header">
<div class="header-left"></div>
<div class="title">主要观点</div>
<div class="header-btn-box">
<div class="btn btnActive">
{{ "核心发现" }}
</div>
<div class="btn">
{{ "政策建议" }}
</div>
</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box3-footer">
<div class="footer-left">
<img src="@/assets/icons/box-footer-left-icon.png" alt="" />
</div>
<div class="footer-center">
{{
`中美经济深度交织,全面脱钩成本高昂且不现实。其核心揭示了三大纽带:生产网络相互依存使强行分离代价巨大;人才双向流动推动创新却成政策博弈焦点;能源领域合作与竞争并存,关乎全球气候治理与经济博弈。报告主张理性竞合,在竞争中找到合作路径。`
}}
</div>
<div class="footer-right">
<img src="@/assets/icons/box-footer-right-icon.png" alt="" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const areaList = ref(["人工智能", "半导体与高科技", "经济与贸易", "地缘政治", "军事与安全", "国际规则与多边体系"]);
</script>
<style lang="scss" scoped>
.wrap {
display: flex;
justify-content: center;
gap: 16px;
.box-header {
width: 100%;
height: 50px;
display: flex;
position: relative;
.header-left {
margin-top: 18px;
width: 8px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 14px;
height: 26px;
line-height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
}
.header-btn-box {
position: absolute;
top: 15px;
right: 83px;
display: flex;
justify-content: flex-end;
gap: 8px;
.btn {
height: 28px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 28px;
}
.btnActive {
color: var(--color-main-active);
border: 1px solid var(--color-main-active);
}
}
.header-right {
position: absolute;
top: 14px;
right: 12px;
display: flex;
justify-content: flex-end;
gap: 4px;
.icon {
width: 28px;
height: 28px;
img {
width: 100%;
height: 100%;
}
}
}
}
.left {
.box1 {
margin-top: 17px;
width: 480px;
height: 390px;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box1-main {
margin: 5px auto;
width: 428px;
height: 282px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
}
.box1-footer {
margin: 0 auto;
width: 108px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.text {
height: 22px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: left;
}
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
}
}
.box2 {
margin-top: 16px;
width: 480px;
height: 390px;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box2-main {
width: 430px;
height: 320px;
margin: 0 auto;
.box2-main-tag-box {
display: flex;
flex-wrap: wrap;
height: 89px;
box-sizing: border-box;
padding: 8px 0;
gap: 8px;
.tag {
height: 28px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
}
}
.box2-content {
width: 430px;
height: 231px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 4px;
background: rgba(247, 248, 249, 1);
}
}
}
}
.right {
margin-top: 17px;
.box3 {
width: 1103px;
height: 936px;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.box3-footer {
position: absolute;
left: 22px;
bottom: 19px;
width: 1057px;
height: 64px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
display: flex;
align-items: center;
justify-content: center;
gap: 13px;
.footer-left {
width: 19px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.footer-center {
width: 964px;
height: 48px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: justify;
}
.footer-right {
width: 24px;
height: 24px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="desc-tab">
<!-- 上半部分:基本信息和经费来源 -->
<div class="top-section">
<!-- 左侧:基本信息卡片 -->
<div class="basic-info-card">
<CardTitle title="基本信息" />
<div class="tank-image">
<img src="https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=400" alt="智库建筑" />
</div>
<div class="info-content">
<div class="info-row">
<span class="label">国家:</span>
<span class="value">{{ thinkTankInfo.country }}</span>
</div>
<div class="info-row">
<span class="label">成立时间:</span>
<span class="value">{{ thinkTankInfo.foundingDate }}</span>
</div>
<div class="info-row">
<span class="label">总部位置:</span>
<span class="value">{{ thinkTankInfo.position }}</span>
</div>
<div class="info-row">
<span class="label">机构性质:</span>
<span class="value">{{ thinkTankInfo.nature }}</span>
</div>
<div class="info-row">
<span class="label">员工数量:</span>
<span class="value">{{ thinkTankInfo.memnum }}</span>
</div>
<div class="info-row">
<span class="label">年度预算:</span>
<span class="value">{{ thinkTankInfo.budget }}</span>
</div>
</div>
<div class="additional-info">
<CardTitle title="全球分支机构" />
<div class="branch-list">
<div class="branch-item" v-for="(item, key) in branchInfo" :key="key">
<span class="location">{{ key }}</span>
<span class="desc">{{ item.join('、') }}</span>
</div>
</div>
</div>
</div>
<!-- 右侧:经费来源 -->
<div class="funding-section">
<FundingSource style="margin-bottom: 20px"/>
<!-- 中间部分:研究领域演变 -->
<div class="timeline-section">
<div class="section-header">
<CardTitle title="研究领域演变" />
<el-icon class="expand-icon"><MoreFilled /></el-icon>
</div>
<div class="timeline-container">
<div class="timeline-periods">
<div class="period-item active" v-for="value in researchAreas" :key="value.id">
<div class="period-title">{{ value.time }}</div>
<div class="period-desc">{{ value.describe }}</div>
</div>
</div>
<div class="timeline-line">
<div class="timeline-progress"></div>
<div class="timeline-dots">
<div class="dot active" v-for="value in researchAreas" :key="value.id"></div>
</div>
</div>
</div>
</div>
<!-- 底部:核心研究人员 -->
<div class="core-researchers-section">
<div class="section-header">
<CardTitle title="核心研究人员" />
<el-icon class="more-icon"><MoreFilled /></el-icon>
</div>
<div class="researchers-content">
<!-- 左侧:树状图 -->
<div class="researchers-treemap">
<div ref="researcherChart" class="researcher-chart"></div>
</div>
<!-- 右侧:人员列表 -->
<div class="researchers-list">
<div
v-for="researcher in coreResearchers"
:key="researcher.id"
class="researcher-item"
>
<div class="researcher-avatar">
<el-image :src="$withFallbackImage(researcher.avatar, researcher.name)" :alt="researcher.name" />
</div>
<div class="researcher-info">
<h4 class="researcher-name">{{ researcher.name }}</h4>
<div class="researcher-previous" v-if="researcher.previousRoles && researcher.previousRoles.length">
<span class="previous-label">之前:</span>
<span class="previous-roles">{{ researcher.previousRoles.join('、') }}</span>
</div>
<p class="researcher-position">{{ researcher.currentPosition }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { MoreFilled } from '@element-plus/icons-vue'
import * as echarts from 'echarts'
import CardTitle from '@/components/CardTitle.vue'
import FundingSource from './FundingSource.vue'
import {
getThinkTankBasicInfo,
getThinkTankBranchInfo,
getThinkTankFundsSource,
getThinkTankFundsTotal,
getThinkTankResearchArea,
getThinkTankPersonList
} from '@/api'
import { useRoute } from 'vue-router'
import {
mockRandBasicInfo,
mockRandBranchInfo,
mockRandFundsSource,
mockRandFundsByType,
mockRandFundsByEntity,
mockRandFundTotal,
mockRandResearchAreas,
mockRandCoreResearchers,
mockRandResearcherCategories
} from '../mockData'
// 组件状态
const activeTimelinePeriod = ref(0)
const fundingChart = ref(null)
const researcherChart = ref(null)
const route = useRoute()
// 经费数据
const fundingData = ref([])
const fundsByType = ref([])
const fundsByEntity = ref([])
// 初始化经费饼图
const initFundingChart = () => {
if (!fundingChart.value) return
const chart = echarts.init(fundingChart.value)
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c}万 ({d}%)'
},
series: [
{
name: '经费来源',
type: 'pie',
radius: ['50%', '70%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false
},
labelLine: {
show: false
},
data: fundingData.value.map(item => ({
value: item.value,
name: item.name,
}))
}
]
}
chart.setOption(option)
const handleResize = () => {
chart.resize()
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
chart.dispose()
}
}
// 初始化研究人员树状图
const initResearcherChart = () => {
if (!researcherChart.value) return
const chart = echarts.init(researcherChart.value)
// 将分类数据转换为树状图格式
const treemapData = []
Object.keys(mockRandResearcherCategories).forEach(category => {
const children = Object.keys(mockRandResearcherCategories[category]).map(item => ({
name: item,
value: mockRandResearcherCategories[category][item]
}))
treemapData.push({
name: category,
value: children.reduce((sum, item) => sum + item.value, 0),
children: children
})
})
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c}人'
},
series: [
{
type: 'treemap',
data: treemapData,
roam: false,
nodeClick: false,
breadcrumb: {
show: false
},
label: {
show: true,
formatter: '{b}\n{c}人',
fontSize: 12
},
upperLabel: {
show: true,
height: 30
},
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
gapWidth: 2
}
}
]
}
chart.setOption(option)
const handleResize = () => {
chart.resize()
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
chart.dispose()
}
}
// 研究领域数据
const researchAreas = ref([])
const thinkTankInfo = ref({})
const getThinkTankInfo = async () => {
thinkTankInfo.value = mockRandBasicInfo
}
const branchInfo = ref({})
const getBranchInfo = async () => {
branchInfo.value = mockRandBranchInfo
}
const getFundsSource = async () => {
fundingData.value = mockRandFundsSource.map(item => ({
value: item.amount,
name: item.institution,
}))
fundsByType.value = mockRandFundsByType
fundsByEntity.value = mockRandFundsByEntity
nextTick(() => {
initFundingChart()
})
}
const fundTotal = ref({
totalJe: 0,
zfJe: 0,
otherJe: 0
})
const getFundTotal = async () => {
fundTotal.value = mockRandFundTotal
}
const getResearchArea = async () => {
researchAreas.value = mockRandResearchAreas
}
const getPersonList = async () => {
coreResearchers.value = mockRandCoreResearchers
nextTick(() => {
initResearcherChart()
})
}
// 初始化
onMounted(async () => {
getThinkTankInfo()
getBranchInfo()
getFundsSource()
getFundTotal()
getResearchArea()
getPersonList()
})
// 核心研究人员数据
const coreResearchers = ref([])
// 格式化货币显示
const formatCurrency = (amount) => {
if (!amount) return '0美元'
const formatted = (amount / 100000000).toFixed(2)
return `${formatted}亿美元`
}
// 格式化金额(万美元)
const formatAmount = (amount) => {
return `${amount}万`
}
</script>
<style scoped lang="scss">
.desc-tab {
min-height: 100vh;
}
/* 上半部分布局 */
.top-section {
display: flex;
gap: 20px;
align-items: flex-start;
margin-bottom: 20px;
}
/* 基本信息卡片 */
.basic-info-card {
min-width: 360px;
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
height: auto;
}
.tank-image {
margin-bottom: 20px;
margin-top: 10px;
}
.tank-image img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 8px;
}
.info-content {
margin-bottom: 24px;
}
.info-row {
display: flex;
margin-bottom: 12px;
align-items: center;
}
.info-row .label {
font-weight: 500;
color: #374151;
min-width: 80px;
font-size: 14px;
}
.info-row .value {
color: #111827;
font-size: 14px;
}
.additional-info {
margin-top: 24px;
padding-top: 24px;
border-top: 1px solid #e5e7eb;
}
.branch-list {
display: flex;
flex-direction: column;
margin-top: 10px;
gap: 8px;
}
.branch-item {
margin-bottom: 8px;
font-size: 14px;
}
.branch-item .location {
font-weight: 500;
color: #374151;
}
.branch-item .desc {
color: #6b7280;
}
/* 经费来源区域 */
.funding-section {
flex: 1;
}
.funding-overview {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.expand-icon {
color: #9ca3af;
cursor: pointer;
font-size: 18px;
}
/* 经费汇总 */
.funding-summary {
display: flex;
gap: 30px;
margin-bottom: 24px;
padding-bottom: 20px;
border-bottom: 1px solid #e5e7eb;
}
.summary-item {
flex: 1;
}
.summary-label {
font-size: 14px;
color: #6b7280;
margin-bottom: 8px;
}
.summary-amount {
font-size: 20px;
font-weight: 700;
color: #1f2937;
}
/* 经费详情 */
.funding-details {
display: flex;
gap: 20px;
align-items: flex-start;
}
.funding-table-left,
.funding-table-right {
flex: 0 0 200px;
display: flex;
flex-direction: column;
gap: 12px;
}
.table-item {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px 0;
border-bottom: 1px solid #f3f4f6;
}
.table-item:last-child {
border-bottom: none;
}
.table-name {
font-size: 13px;
color: #374151;
line-height: 1.4;
}
.table-amount {
font-size: 14px;
font-weight: 600;
color: #111827;
}
.table-percent {
font-size: 12px;
color: #6b7280;
}
.funding-chart-wrapper {
flex: 1;
min-height: 300px;
display: flex;
align-items: center;
justify-content: center;
}
.funding-chart {
width: 100%;
height: 300px;
min-height: 300px;
}
/* 研究领域演变 */
.timeline-section {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
margin-bottom: 20px;
}
.timeline-container {
position: relative;
}
.timeline-periods {
display: flex;
justify-content: space-between;
margin-bottom: 40px;
}
.period-item {
flex: 1;
padding: 0 16px;
text-align: center;
}
.period-item.active .period-title {
color: #3b82f6;
font-weight: 600;
}
.period-title {
font-size: 16px;
font-weight: 500;
color: #374151;
margin-bottom: 8px;
}
.period-desc {
font-size: 13px;
color: #6b7280;
line-height: 1.4;
}
.timeline-line {
position: relative;
height: 4px;
background-color: #e5e7eb;
border-radius: 2px;
margin: 0 60px;
}
.timeline-progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
background: linear-gradient(90deg, #3b82f6 0%, #1d4ed8 100%);
border-radius: 2px;
}
.timeline-dots {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
display: flex;
justify-content: space-between;
}
.dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #e5e7eb;
border: 2px solid white;
box-shadow: 0 0 0 2px #e5e7eb;
}
.dot.active {
background-color: #3b82f6;
box-shadow: 0 0 0 2px #3b82f6;
}
/* 核心研究人员 */
.core-researchers-section {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
margin-bottom: 30px;
}
.more-icon {
color: #6b7280;
cursor: pointer;
font-size: 18px;
}
.more-icon:hover {
color: #374151;
}
.researchers-content {
display: flex;
gap: 30px;
align-items: flex-start;
}
.researchers-treemap {
flex: 1;
min-height: 400px;
}
.researcher-chart {
width: 100%;
height: 400px;
min-height: 400px;
}
.researchers-list {
flex: 0 0 400px;
display: flex;
flex-direction: column;
gap: 20px;
}
.researcher-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid #f3f4f6;
}
.researcher-item:last-child {
border-bottom: none;
}
.researcher-avatar {
flex-shrink: 0;
}
.researcher-avatar .el-image {
width: 48px;
height: 48px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #f3f4f6;
}
.researcher-info {
flex: 1;
min-width: 0;
}
.researcher-name {
font-size: 16px;
font-weight: 600;
color: #111827;
margin: 0 0 6px 0;
line-height: 1.3;
}
.researcher-previous {
font-size: 12px;
color: #6b7280;
margin-bottom: 4px;
}
.previous-label {
color: #9ca3af;
}
.previous-roles {
color: #6b7280;
}
.researcher-position {
font-size: 13px;
color: #374151;
margin: 0;
line-height: 1.4;
word-wrap: break-word;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.top-section {
flex-direction: column;
}
.funding-details {
flex-direction: column;
}
.funding-table-left,
.funding-table-right {
flex: none;
width: 100%;
}
.funding-chart-wrapper {
min-height: 250px;
}
.funding-chart {
height: 250px;
min-height: 250px;
}
.timeline-periods {
flex-direction: column;
gap: 20px;
}
.timeline-line {
display: none;
}
.researchers-content {
flex-direction: column;
}
.researchers-list {
flex: none;
width: 100%;
}
}
@media (max-width: 768px) {
.desc-tab {
padding: 16px;
}
.basic-info-card,
.funding-overview,
.timeline-section,
.core-researchers-section {
padding: 16px;
}
.researcher-item {
gap: 10px;
padding: 8px 0;
}
.researcher-avatar .el-image {
width: 40px;
height: 40px;
}
.researcher-name {
font-size: 14px;
}
.researcher-position {
font-size: 12px;
}
}
</style>
\ No newline at end of file
<template>
<div class="funding-source-container">
<div class="chart-header">
<CardTitle title="经费来源" />
<div class="header-icons">
<el-icon><Coin /></el-icon>
<el-icon><Download /></el-icon>
<el-icon><Star /></el-icon>
</div>
</div>
<div class="chart-body">
<div class="stats-panel">
<div class="stat-card total-card">
<div class="label">总计</div>
<div class="value">4.358亿美元</div>
</div>
<div class="stat-card govt-card">
<div class="label">政府部门</div>
<div class="value">3.271亿美元</div>
</div>
<div class="stat-card other-card">
<div class="label">其他机构</div>
<div class="value">1.087亿美元</div>
</div>
</div>
<div class="chart-panel" ref="chartRef"></div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import * as echarts from 'echarts';
import { Coin, Download, Star } from '@element-plus/icons-vue';
import CardTitle from '@/components/CardTitle.vue';
// 1. 模拟数据
// 注意:为了还原图表,这里的数据是凑出来的近似值,确保比例看起来像截图
const chartData = [
// 右侧数据 (通常从12点顺时针开始)
{ value: 7830, name: '美国国土安全部', percent: '21%' },
{ value: 7290, name: '美国办公室国防部长和...', percent: '21%' },
{ value: 6740, name: '美国卫生与公众服务部...', percent: '18%' },
{ value: 4840, name: '美国空军', percent: '18%' },
{ value: 3880, name: '美国陆军', percent: '16%' },
{ value: 3520, name: '捐款', percent: '16%' },
// 左侧数据
{ value: 3110, name: '基金', percent: '14%' },
{ value: 2905, name: '大学', percent: '12%' },
{ value: 2840, name: '私营部门', percent: '12%' },
{ value: 2400, name: '州和地方政府机构', percent: '12%' },
{ value: 2130, name: '其他非营利组织', percent: '11%' },
{ value: 2060, name: '非美国政府机构和国际...', percent: '8%' },
{ value: 1850, name: '其他联邦机构', percent: '8%' },
{ value: 1200, name: '其他', percent: '8%' },
];
// 颜色盘 (从截图吸取的近似色)
const colorPalette = [
'#8cbbf1', // 浅蓝
'#a5d67d', // 浅绿
'#f6c469', // 橙黄
'#fdf27e', // 黄色
'#94e6d6', // 青绿
'#6b85ef', // 深蓝紫
'#d3d7fd', // 极浅蓝
'#d9f3b2', // 极浅绿
'#eb7d7d', // 红
'#a28ee3', // 紫
'#f4a678', // 橙
'#6ba7f5', // 蓝
'#f5a8a8', // 浅红
];
const chartRef = ref(null);
let myChart = null;
const initChart = () => {
if (!chartRef.value) return;
myChart = echarts.init(chartRef.value);
const option = {
color: colorPalette,
tooltip: {
trigger: 'item',
formatter: '{b}: {c}万 ({d}%)'
},
series: [
{
name: '经费来源',
type: 'pie',
radius: ['45%', '60%'], // 环形图半径
center: ['50%', '50%'], // 居中
data: chartData,
// 标签配置
label: {
show: true,
position: 'outside',
formatter: function (params) {
// 这里的逻辑是为了模仿截图:右边的文字名字在右侧,左边的文字名字在左侧
// 简单的判断逻辑:基于 ECharts 内部计算的 label 角度,或者根据数据索引
// 这里我们构建一个富文本结构
return `{name|${params.name}}\n{val|${params.value}万} {pct|${params.data.percent}}`;
},
// 关键配置:使用 edge 对齐方式让标签像表格一样排列在两侧
alignTo: 'edge',
edgeDistance: 10, // 距离容器边缘的距离
minMargin: 5,
lineHeight: 20,
rich: {
name: {
fontSize: 13,
fontWeight: 'bold',
color: '#333',
padding: [0, 5]
},
val: {
fontSize: 12,
color: '#666'
},
pct: {
fontSize: 12,
color: '#666',
padding: [0, 5]
}
}
},
// 引导线配置
labelLine: {
length: 15,
length2: 60, // 第二段线长一点,以便连接到边缘
maxSurfaceAngle: 80
},
// 每一项的样式
itemStyle: {
borderColor: '#fff',
borderWidth: 2
}
}
]
};
myChart.setOption(option);
};
// 响应式处理
const resizeHandler = () => {
if (myChart) {
myChart.resize();
}
};
onMounted(() => {
nextTick(() => {
initChart();
window.addEventListener('resize', resizeHandler);
});
});
onUnmounted(() => {
window.removeEventListener('resize', resizeHandler);
if (myChart) {
myChart.dispose();
}
});
</script>
<style lang="scss" scoped>
.funding-source-container {
width: 100%;
background: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
// 头部样式
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.title-wrapper {
display: flex;
align-items: center;
.blue-bar {
width: 4px;
height: 18px;
background-color: #409eff; // Element Plus Primary Blue
margin-right: 8px;
border-radius: 2px;
}
.title-text {
font-size: 18px;
font-weight: 700;
color: #303133;
}
}
.header-icons {
display: flex;
gap: 15px;
color: #909399;
cursor: pointer;
.el-icon {
font-size: 18px;
&:hover {
color: #409eff;
}
}
}
}
// 主体布局
.chart-body {
display: flex;
flex-direction: row;
height: 450px; // 固定一个高度给图表展示
// 左侧统计面板
.stats-panel {
width: 200px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 20px;
flex-shrink: 0;
.stat-card {
padding: 15px;
border-radius: 6px;
display: flex;
flex-direction: column;
gap: 8px;
.label {
font-size: 14px;
}
.value {
font-size: 20px;
font-weight: 800;
}
// 不同卡片的特定样式
&.total-card {
background-color: #eef6ff; // 浅蓝背景
.label { color: #409eff; }
.value { color: #185ebd; }
}
&.govt-card {
background-color: #fff2f2; // 浅红背景
.label { color: #f56c6c; }
.value { color: #c43e3e; }
}
&.other-card {
background-color: #f0f9eb; // 浅绿背景
.label { color: #67c23a; }
.value { color: #3a8e1e; }
}
}
}
// 右侧图表区域
.chart-panel {
flex: 1;
min-width: 0; // 防止 flex 子项溢出
height: 100%;
}
}
}
// 移动端适配微调
@media (max-width: 768px) {
.chart-body {
flex-direction: column !important;
height: auto !important;
.stats-panel {
width: 100% !important;
flex-direction: row !important;
overflow-x: auto;
padding-bottom: 10px;
}
.chart-panel {
height: 400px !important;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="wrap">
<div class="top">
<div class="box1 box">
<div class="box-header">
<div class="icon">
<img src="./images/box-header-icon1.png" alt="" />
</div>
<div class="title">{{ "提出建议领域分布" }}</div>
<div class="box-header-right">{{ "查看数据源 >" }}</div>
</div>
<div class="box-main">
<div class="select-box">
<el-select v-model="box1SelectYear" placeholder="选择时间" style="width: 100px">
<el-option v-for="item in box1YearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div id="box1Chart"></div>
</div>
</div>
<div class="box2 box">
<div class="box-header">
<div class="icon">
<img src="./images/box-header-icon2.png" alt="" />
</div>
<div class="title">{{ "相关政策领域分布" }}</div>
<div class="box-header-right">{{ "查看数据源 >" }}</div>
</div>
<div class="box-main">
<div class="select-box">
<el-select v-model="box2SelectYear" placeholder="选择时间" style="width: 100px">
<el-option v-for="item in box2YearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div id="box2Chart">
<div class="box2-item" v-for="(item, index) in box2Data" :key="index">
<div class="icon" :style="{ background: item.color }"></div>
<div class="name">{{ item.name }}</div>
<el-progress
:show-text="false"
:color="item.color"
style="width: 220px"
stroke-width="8"
:percentage="item.per"
/>
<div class="num">{{ item.value + "项" }} / {{ item.total + "项" }}</div>
<div class="per">{{ item.per + "%" }}</div>
</div>
<div class="box2-item">
<el-progress :percentage="50" />
</div>
</div>
</div>
</div>
<div class="box3 box">
<div class="box-header">
<div class="icon">
<img src="./images/box-header-icon2.png" alt="" />
</div>
<div class="title">{{ "热门研究方向变化趋势" }}</div>
<div class="box-header-right">{{ "查看数据源 >" }}</div>
</div>
<div class="box-main">
<div class="select-box">
<el-select v-model="box3SelectYear" placeholder="选择时间" style="width: 100px">
<el-option v-for="item in box3YearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div id="box3Chart"></div>
</div>
</div>
</div>
<div class="bottom">
<div class="bottom-header">
<div class="search-box">
<el-input placeholder="搜索政策建议" v-model="searchPolicy" style="width: 270px" />
<div class="icon">
<img src="./images/search-icon.png" alt="" />
</div>
</div>
<div class="select-box">
<el-select v-model="selectedYear" placeholder="选择时间" style="width: 120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<div class="bottom-main">
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "科技领域" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox
v-for="(item, index) in areaList"
:key="index"
v-model="selectedAreaList"
:label="item"
class="filter-checkbox"
>
{{ item }}
</el-checkbox>
</div>
</div>
</div>
</div>
<div class="right">
<div class="right-main">
<div class="right-main-item" v-for="(item, index) in policyList" :key="index">
<div class="item-left">
<img :src="item.img" alt="" />
</div>
<div class="item-right">
<div class="title">{{ item.title }}</div>
<div class="info">{{ item.time }} · {{ item.desc }}</div>
<div class="tag-box">
<div class="tag" v-for="(tag, idx) in item.tagList" :key="idx">
{{ tag }}
</div>
</div>
<div class="file-box">
<div class="file" v-for="(file, idxx) in item.fileList" :key="idxx">
<div class="type">{{ file.type }}</div>
<div class="title">{{ file.title }}</div>
<div class="more">
<img src="./images/arrow-right.png" alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="info">
{{ `共105项调查` }}
</div>
<div class="page-box">
<el-pagination :page-size="10" background layout="prev, pager, next" :total="120" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import setChart from "@/utils/setChart";
import getPieChart from "./utils/piechart";
import getMultiLineChart from "./utils/multiLineChart";
import Img1 from "./images/img1.png";
import Img2 from "./images/img2.png";
import Img3 from "./images/img3.png";
import Img4 from "./images/img4.png";
const box1Data = ref([
{
name: "半导体",
value: 50,
color: "#4096FF"
},
{
name: "电子设备",
value: 46,
color: "#FFA39E"
},
{
name: "显示技术",
value: 40,
color: "#ADC6FF"
},
{
name: "新能源",
value: 32,
color: "#FFC069"
},
{
name: "通信设备",
value: 31,
color: "#B5F5EC"
},
{
name: "汽车",
value: 30,
color: "#B37FEB"
},
{
name: "其他",
value: 24,
color: "#D6E4FF"
}
]);
const box1SelectYear = ref("2025");
const box1YearList = ref([
{
label: "2025",
value: "2025"
},
{
label: "2024",
value: "2024"
},
{
label: "2023",
value: "2023"
}
]);
const box2Data = ref([
{
name: "集成电路",
value: 2,
total: 10,
per: 20,
color: "#CE4F51"
},
{
name: "人工智能",
value: 1,
total: 6,
per: 17,
color: "#055FC2"
},
{
name: "通信网络",
value: 2,
total: 7,
per: 26,
color: "#13A8A8"
},
{
name: "量子科技",
value: 1,
total: 2,
per: 50,
color: "#722ED1"
},
{
name: "生物科技",
value: 3,
total: 7,
per: 43,
color: "#FA8C16"
},
{
name: "能源",
value: 11,
total: 20,
per: 55,
color: "#69B1FF"
}
]);
const box2SelectYear = ref("2025");
const box2YearList = ref([
{
label: "2025",
value: "2025"
},
{
label: "2024",
value: "2024"
},
{
label: "2023",
value: "2023"
}
]);
const box3Data = ref({
title: ["2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"],
data: [
{
name: "人工智能",
value: [73, 32, 42, 48, 38, 49, 63, 75, 70, 86, 95, 87]
},
{
name: "集成电路",
value: [8, 3, 2, 8, 9, 10, 12, 18, 16, 18, 20, 22]
},
{
name: "量子科技",
value: [1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3]
}
]
});
const box3SelectYear = ref("2025");
const box3YearList = ref([
{
label: "2025",
value: "2025"
},
{
label: "2024",
value: "2024"
},
{
label: "2023",
value: "2023"
}
]);
const searchPolicy = ref("");
const yearList = ref([
{
label: "近一年发布",
value: "近一年发布"
},
{
label: "近两年发布",
value: "近两年发布"
},
{
label: "近三年发布",
value: "近三年发布"
}
]);
const selectedYear = ref("近一年发布");
const areaList = ref(["人工智能", "半导体/芯片", "能源与气候", "国际关系", "经济政策", "国防与安全"]);
const selectedAreaList = ref(["人工智能", "半导体/芯片"]);
const policyList = ref([
{
img: Img1,
title: "创建并定制针对人工智能技术的验证、确认与评估技术。",
time: "2025年6月26日",
desc: "保持美国在人工智能与机器学习领域的优势",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《芯片科学法案》"
},
{
type: "法案",
title: "2024《芯片科学法案》"
}
]
},
{
img: Img1,
title: "为运用人工智能的新作战概念建立开发、测试与评估流程。",
time: "2025年6月26日",
desc: "保持美国在人工智能与机器学习领域的优势",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《芯片科学法案》"
},
{
type: "法案",
title: "2024《芯片科学法案》"
}
]
},
{
img: Img1,
title: "通过制定和维护一个前瞻性的人工智能发展路线图来管理预期,该路线图应阐明国防部在近期(一至两年)、中期(三至五年)和远期(六至十年)部署人工智能的现实目标",
time: "2025年6月26日",
desc: "保持美国在人工智能与机器学习领域的优势",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《芯片科学法案》"
},
{
type: "政令",
title: "2025《关于优化美军作战决策结构的建议》"
}
]
},
{
img: Img2,
title: "考虑采取更全面的方法来打击全球供应链中强迫劳动使用的选项。",
time: "2025年3月15日",
desc: " 美国贸易执法是否发挥了作用,能否做得更多?",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《维吾尔强迫劳动预防法》"
}
]
},
{
img: Img2,
title: "与利益相关者共同收集证据,为关于贸易执法的公共讨论提供信息。",
time: "2025年3月15日",
desc: "美国贸易执法是否发挥了作用,能否做得更多?",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《维吾尔强迫劳动预防法》"
}
]
},
{
img: Img2,
title: "推动清洁能源生产供内用,化石燃料重新配置出口。",
time: "2025年6月26日",
desc: "美国贸易执法是否发挥了作用,能否做得更多?",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《重塑美国人口结构法案》"
},
{
type: "法案",
title: "2024《重塑美国人口结构法案》"
}
]
},
{
img: Img2,
title: "允许OPT的国际学生出国旅行并持多次入境签证重新进入美国。",
time: "2025年6月26日",
desc: "中美经济竞争:复杂经济和地缘政治关系中的收益与风险",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《重塑美国人口结构法案》"
},
{
type: "法案",
title: "2025《开放人才法案》"
}
]
},
{
img: Img3,
title: "增加中国公民可获得的 H-1B 签证数量。",
time: "2025年6月26日",
desc: "中美经济竞争:复杂经济和地缘政治关系中的收益与风险",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《重塑美国人口结构法案》"
},
{
type: "法案",
title: "2025《GENIUS法案》"
}
]
},
{
img: Img3,
title: "通过就业偏好类别增加绿卡的数量。",
time: "2025年6月26日",
desc: "中美经济竞争:复杂经济和地缘政治关系中的收益与风险",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《重塑美国人口结构法案》"
},
{
type: "法案",
title: "2025《开放人才法案》"
}
]
},
{
img: Img4,
title: "推动清洁能源生产供内用,化石燃料重新配置出口。",
time: "2025年6月26日",
desc: "美国传统能源产业的未来:是走向终结还是持续发展?",
tagList: ["人工智能", "集成电路"],
fileList: [
{
type: "法案",
title: "2024《能源法案》"
}
]
}
]);
onMounted(() => {
const box1Chart = getPieChart(box1Data.value);
setChart(box1Chart, "box1Chart");
let box3Chart = getMultiLineChart(
box3Data.value.title,
box3Data.value.data[0].value,
box3Data.value.data[1].value,
box3Data.value.data[2].value
);
setChart(box3Chart, "box3Chart");
});
</script>
<style lang="scss" scoped>
.wrap {
width: 100%;
height: 100%;
.top {
height: 420px;
width: 1600px;
margin: 24px 0;
display: flex;
gap: 16px;
.box {
width: 520px;
height: 420px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box-header {
width: 520px;
height: 48px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
display: flex;
position: relative;
.icon {
width: 20px;
height: 20px;
margin-top: 15px;
margin-left: 22px;
img {
width: 100%;
height: 100%;
}
}
.title {
margin-top: 11px;
margin-left: 18px;
height: 26px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.box-header-right {
position: absolute;
top: 12px;
right: 27px;
height: 24px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: right;
}
}
.box-main {
height: 360px;
position: relative;
overflow: hidden;
#box1Chart {
height: 360px;
}
#box2Chart {
width: 470px;
margin: 0 auto;
margin-top: 50px;
height: 300px;
.box2-item {
height: 30px;
margin-top: 20px;
display: flex;
align-items: center;
.icon {
width: 12px;
height: 12px;
border-radius: 6px;
}
.name {
width: 80px;
margin-left: 7px;
margin-right: 23px;
height: 24px;
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 {
width: 80px;
margin-left: 10px;
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
text-align: right;
}
.per {
margin-left: 5px;
height: 22px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
#box3Chart {
height: 360px;
}
.select-box {
position: absolute;
z-index: 999;
top: 13px;
right: 33px;
width: 100px;
height: 28px;
}
}
}
}
.bottom {
width: 1600px;
.bottom-header {
height: 48px;
display: flex;
justify-content: space-between;
.search-box {
display: flex;
width: 300px;
height: 32px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
.icon {
width: 16px;
height: 16px;
margin: 8px 7px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
.select-box {
margin-right: 5px;
}
}
.bottom-main {
display: flex;
justify-content: space-between;
.left {
width: 300px;
height: 483px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.select-box {
margin-top: 21px;
.select-box-header {
display: flex;
gap: 17px;
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
.title {
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
}
.select-main {
margin-left: 25px;
width: 160px;
}
.select-main1 {
width: 100px;
}
}
}
.right {
width: 1284px;
height: 1670px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.right-main {
margin: 17px auto;
width: 1209px;
height: 1540px;
.right-main-item {
height: 154px;
box-sizing: border-box;
padding-top: 8px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
display: flex;
.item-left {
width: 57px;
height: 77px;
margin-top: 3px;
img {
width: 100%;
height: 100%;
}
}
.item-right {
margin-left: 15px;
.title {
// height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.info {
margin-top: 7px;
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: left;
}
.tag-box {
margin-top: 7px;
display: flex;
gap: 8px;
.tag {
height: 22px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(247, 248, 249, 1);
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
}
.file-box {
margin-top: 7px;
display: flex;
gap: 8px;
.file {
height: 32px;
padding: 0 8px;
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
border-radius: 4px;
background: rgba(246, 250, 255, 1);
.type {
height: 22px;
padding: 0 4px;
border-radius: 4px;
background: rgba(231, 243, 255, 1);
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
.title {
height: 24px;
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
.more {
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
}
}
}
}
}
.right-footer {
margin: 0 auto;
width: 1209px;
height: 96px;
display: flex;
justify-content: space-between;
.info {
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
}
}
</style>
\ No newline at end of file
import * as echarts from 'echarts'
const getMultiLineChart = (dataX, dataY1, dataY2, dataY3) => {
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985'
}
}
},
grid: {
top: '15%',
right: '5%',
bottom: '5%',
left: '5%',
containLabel: true
},
legend: {
show: true,
top: 10,
left:'10%'
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: dataX
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
name: '人工智能',
type: 'line',
emphasis: {
focus: 'series'
},
data: dataY1
},
{
name: '集成电路',
type: 'line',
emphasis: {
focus: 'series'
},
data: dataY2
},
{
name: '量子科技',
type: 'line',
emphasis: {
focus: 'series'
},
data: dataY3
}
]
}
}
export default getMultiLineChart
\ No newline at end of file
const getPieChart = (data) => {
let option = {
series: [
{
type: 'pie',
radius: [70, 100],
height: '100%',
left: 'center',
width: '100%',
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
label: {
alignTo: 'edge',
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
minMargin: 5,
edgeDistance: 10,
lineHeight: 15,
rich: {
time: {
fontSize: 10,
color: '#999'
}
}
},
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
},
labelLayout: function (params) {
const isLeft = params.labelRect.x < 556 / 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: data
}]
}
return option
}
export default getPieChart;
\ No newline at end of file
<template>
<div class="page-container">
<!-- Header 占满宽度 -->
<header class="think-tank-header">
<div class="header-container">
<div class="header-left">
<el-image :src="$withFallbackImage(summary.logo, summary.name) " :alt="summary.name" class="logo" />
<div class="info">
<h1 class="title"> {{ summary.name }} <span class="subtitle">{{ summary.ename }}</span></h1>
<p class="description">
{{ summary.describe }}
</p>
<div class="tags">
<el-tag effect="light" class="custom-tag" v-for="tag in summary.tags" :key="tag">{{ tag }}</el-tag>
<div class="wrap">
<div class="header">
<div class="header-top">
<div class="header-top-left">
<img src="" alt="" />
</div>
<div class="header-top-center">
<div class="center-top">
<div class="name">{{ "兰德公司" }}</div>
<div class="e-name">{{ "RAND Corporation" }}</div>
</div>
<div class="center-center">
{{ "全球顶尖政策研究机构,专注于国家安全、科技政策、医疗卫生、能源政策、公共安全等领域的研究。 " }}
</div>
<div class="header-right">
<el-button type="primary" :icon="Collection">查看报告合集</el-button>
<div class="center-footer">
<div class="tag">{{ "国家安全" }}</div>
<div class="tag">{{ "科技政策" }}</div>
</div>
</div>
<!-- Tab切换区域 -->
<div class="header-tabs">
<div class="tabs-container">
<el-tabs v-model="activeTab" class="header-tab-nav">
<el-tab-pane label="智库报告" name="reports"></el-tab-pane>
<el-tab-pane label="政策追踪" name="policy"></el-tab-pane>
<el-tab-pane label="智库百科" name="wiki"></el-tab-pane>
</el-tabs>
<!-- <div class="header-top-right">{{ '查看智库官网' }}</div> -->
</div>
<div class="header-footer">
<div class="tab" :class="{ tabActive: tabActiveName === '智库动态' }" @click="switchTab('智库动态')">
<div class="icon">
<img v-show="tabActiveName === '智库动态'" src="./images/tab-icon-active.png" alt="" />
<img v-show="tabActiveName !== '智库动态'" src="./images/tab-icon.png" alt="" />
</div>
</header>
<!-- Main Content 最大宽度1000px并居中 -->
<main class="main-content">
<div class="content-container">
<!-- 根据activeTab切换组件 -->
<component :is="currentComponent" :thinkTankName="summary.name" />
<div class="text" :class="{ textActive: tabActiveName === '智库动态' }">{{ "智库动态" }}</div>
</div>
<div class="tab" :class="{ tabActive: tabActiveName === '政策追踪' }" @click="switchTab('政策追踪')">
<div class="icon">
<img v-show="tabActiveName === '政策追踪'" src="./images/tab-icon2-active.png" alt="" />
<img v-show="tabActiveName !== '政策追踪'" src="./images/tab-icon2.png" alt="" />
</div>
<div class="text" :class="{ textActive: tabActiveName === '政策追踪' }">{{ "政策追踪" }}</div>
</div>
<div class="tab" :class="{ tabActive: tabActiveName === '智库百科' }" @click="switchTab('智库百科')">
<div class="icon">
<img v-show="tabActiveName === '智库百科'" src="./images/tab-icon3-active.png" alt="" />
<img v-show="tabActiveName !== '智库百科'" src="./images/tab-icon3.png" alt="" />
</div>
<div class="text" :class="{ textActive: tabActiveName === '智库百科' }">{{ "智库百科" }}</div>
</div>
</div>
</div>
<div class="main">
<ThinkInfo v-if="tabActiveName === '智库百科'"></ThinkInfo>
<PolicyTracking v-if="tabActiveName === '政策追踪'"></PolicyTracking>
<ThinkDaynamics v-if="tabActiveName === '智库动态'"></ThinkDaynamics>
</div>
</main>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue';
import { Collection } from '@element-plus/icons-vue';
import ReportTab from './ReportTab.vue';
import PolicyTab from '@/components/PolicyTab.vue';
import DescTab from './DescTab.vue';
import { getThinkTankSummary } from '@/api';
import { useRoute } from 'vue-router';
import { mockThinkTankList } from '../mockData';
// --- Component State ---
const activeTab = ref('reports');
const route = useRoute();
import { ref, reactive, computed, onMounted } from "vue";
// --- Component Map ---
const componentMap = {
reports: ReportTab,
policy: PolicyTab,
wiki: DescTab
};
import ThinkDaynamics from "./thinkDynamics/index.vue"
import PolicyTracking from "./PolicyTracking/index.vue";
import ThinkInfo from "./thinkInfo/index.vue";
const summary = ref({
name: '',
describe: '',
ename: '',
url: '',
tags: []
});
// --- Computed Properties ---
const currentComponent = computed(() => {
return componentMap[activeTab.value] || ReportTab;
});
onMounted(() => {
getThinkTankSummary({ id: route.params.id }).then(res => {
console.log(res.data);
// summary.value = res.data || {};
summary.value = mockThinkTankList[0];
});
});
</script>
<style scoped>
/* 变量 1200px - 定义在组件根元素上 */
.page-container {
--max-width: 1650px;
background-color: #f5f7fa;
font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;
min-height: 100vh;
width: 100%;
}
const tabActiveName = ref("智库动态");
const switchTab = name => {
tabActiveName.value = name;
};
/* Header Styles - 占满宽度 */
.think-tank-header {
background-color: #fff;
width: 100%;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
</script>
.header-container {
max-width: var(--max-width);
margin: 0 auto;
padding: 24px 24px 0;
<style lang="scss" scoped>
.wrap {
width: 1920px;
height: 984px;
.header {
width: 1920px;
height: 188px;
box-sizing: border-box;
padding: 0 160px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.header-top {
margin-top: 24px;
display: flex;
justify-content: space-between;
align-items: flex-start;
}
/* Header Tabs区域 */
.header-tabs {
background-color: #fff;
/* border-top: 1px solid #e4e7ed; */
width: 100%;
margin-top: 20px;
}
.tabs-container {
max-width: var(--max-width);
margin: 0 auto;
padding: 0 24px;
}
.header-tab-nav {
margin: 0;
}
.header-tab-nav :deep(.el-tabs__header) {
margin: 0;
border-bottom: none;
}
.header-tab-nav :deep(.el-tabs__nav-wrap) {
padding: 0;
}
.header-tab-nav :deep(.el-tabs__nav-wrap::after) {
display: none;
}
.header-tab-nav :deep(.el-tabs__item) {
font-size: 16px;
font-weight: 500;
color: #606266;
padding: 16px 24px;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
}
.header-tab-nav :deep(.el-tabs__item:hover) {
color: #409eff;
}
.header-tab-nav :deep(.el-tabs__item.is-active) {
color: #409eff;
border-bottom-color: #409eff;
font-weight: 600;
}
.header-tab-nav :deep(.el-tabs__active-bar) {
display: none;
}
.header-left {
.header-top-left {
width: 88px;
height: 88px;
background: purple;
}
.header-top-center {
margin-left: 22px;
.center-top {
display: flex;
align-items: flex-start;
}
.logo {
width: 64px;
height: 64px;
border-radius: 8px;
margin-right: 20px;
flex-shrink: 0;
}
.info .title {
.name {
height: 26px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 600;
color: #303133;
margin: 0 0 8px 0;
}
.info .subtitle {
font-size: 14px;
color: #909399;
font-weight: normal;
margin-left: 8px;
}
.info .description {
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.e-name {
margin-left: 11px;
height: 26px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
color: #606266;
line-height: 1.6;
margin: 0 0 12px 0;
max-width: 600px;
}
.custom-tag {
background-color: #ecf5ff;
border-color: #d9ecff;
color: #409eff;
margin-right: 8px;
}
/* Main Content Styles - 最大宽度1000px并居中 */
.main-content {
width: 100%;
font-weight: 400;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
}
.center-center {
margin-top: 4px;
width: 769px;
height: 24px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
}
.center-footer {
margin-top: 9px;
display: flex;
gap: 8px;
.tag {
height: 26px;
line-height: 26px;
text-align: center;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
background: rgba(246, 250, 255, 1);
color: rgba(5, 95, 194, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
}
}
}
}
.header-footer {
width: 340px;
height: 48px;
position: absolute;
bottom: 0;
left: 160px;
display: flex;
gap: 24px;
.tab {
width: 92px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 24px;
}
.content-container {
max-width: var(--max-width);
gap: 4px;
cursor: pointer;
border-bottom: 2px solid transparent;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
background-color: #fff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
/* 响应式设计 */
@media (max-width: 1240px) {
.header-container {
padding: 24px 20px 0;
height: 100%;
}
.tabs-container {
padding: 0 20px;
}
}
@media (max-width: 1040px) {
.content-container {
margin: 0 20px;
.text {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 400;
line-height: 24px;
}
.main-content {
padding: 0;
.textActive {
color: rgba(5, 95, 194, 1);
font-weight: 700;
}
}
@media (max-width: 768px) {
.header-container {
flex-direction: column;
gap: 16px;
padding: 16px 16px 0;
}
.tabs-container {
padding: 0 16px;
.tabActive {
border-bottom: 2px solid rgba(5, 95, 194, 1);
}
.header-tab-nav :deep(.el-tabs__item) {
padding: 12px 16px;
font-size: 14px;
}
.header-left {
flex-direction: column;
align-items: flex-start;
}
.logo {
margin-right: 0;
margin-bottom: 12px;
.main {
margin: 0 auto;
width: 1600px;
.main-header {
height: 64px;
display: flex;
justify-content: space-between;
.search-box {
margin-top: 16px;
display: flex;
width: 300px;
height: 32px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
.icon {
width: 16px;
height: 16px;
margin: 8px 7px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
.header-right {
align-self: flex-start;
}
.content-container {
margin: 0 16px;
padding: 16px;
}
.info .description {
max-width: none;
.select-box {
margin-top: 16px;
margin-right: 5px;
}
}
@media (max-width: 480px) {
.header-container {
padding: 12px 12px 0;
}
.tabs-container {
padding: 0 12px;
.main-content {
display: flex;
gap: 16px;
.left {
width: 300px;
height: 806px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(94, 95, 95, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.select-box {
margin-top: 21px;
.select-box-header {
display: flex;
gap: 17px;
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
.header-tab-nav :deep(.el-tabs__item) {
padding: 10px 12px;
font-size: 13px;
.title {
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
.content-container {
margin: 0 12px;
padding: 12px;
}
.info .title {
.select-main {
margin-left: 25px;
}
.select-main1 {
width: 100px;
}
}
}
.right {
width: 1284px;
height: 1377px;
.card-box {
width: 1284px;
height: 1248px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.footer-card {
width: 418px;
height: 300px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.footer-card-top {
width: 384px;
height: 206px;
margin: 0 auto;
margin-top: 15px;
img {
width: 100%;
height: 100%;
}
}
.footer-card-title {
margin: 0 auto;
margin-top: 13px;
width: 376px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 24px;
}
.info .subtitle {
display: block;
margin-left: 0;
margin-top: 4px;
.footer-card-footer {
margin: 0 auto;
margin-top: 5px;
width: 376px;
height: 22px;
display: flex;
justify-content: space-between;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.right-footer {
margin-top: 43px;
display: flex;
justify-content: space-between;
.info {
height: 19px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
}
}
:deep(.el-input__wrapper) {
box-shadow: none;
border-radius: 10px;
}
:deep(.el-input__wrapper:hover) {
box-shadow: none !important;
}
:deep(.el-input__wrapper.is-focus) {
box-shadow: none !important;
}
</style>
\ No newline at end of file
<template>
<div class="wrap">
<div class="main-header">
<div class="search-box">
<el-input placeholder="搜索智库报告" v-model="searchReport" style="width: 270px" />
<div class="icon">
<img src="./images/search-icon.png" alt="" />
</div>
</div>
<div class="select-box">
<el-select v-model="selectedYear" placeholder="选择时间" style="width: 120px">
<el-option v-for="item in yearList" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
<div class="main-content">
<div class="left">
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "地区筛选" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox
v-for="region in filters.regions"
:key="region.id"
v-model="activeFilters.regions"
:label="region.id"
class="filter-checkbox"
>
{{ region.name }}
</el-checkbox>
</div>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "智库类型" }}</div>
</div>
<div class="select-main select-main1">
<div class="checkbox-group">
<el-checkbox
v-for="type in filters.types"
:key="type.id"
v-model="activeFilters.types"
:label="type.id"
class="filter-checkbox"
>
{{ type.name }}
</el-checkbox>
</div>
</div>
</div>
<div class="select-box">
<div class="select-box-header">
<div class="icon"></div>
<div class="title">{{ "研究领域" }}</div>
</div>
<div class="select-main">
<div class="checkbox-group">
<el-checkbox
v-for="research in filters.researches"
:key="research.id"
v-model="activeFilters.researches"
:label="research.id"
class="filter-checkbox"
>
{{ research.name }}
</el-checkbox>
</div>
</div>
</div>
</div>
<div class="right">
<div class="card-box">
<div class="footer-card" v-for="(item, index) in curFooterList" :key="index">
<div class="footer-card-top">
<img :src="item.img" alt="" />
</div>
<div class="footer-card-title">
{{ item.title }}
</div>
<div class="footer-card-footer">
<div class="time">{{ item.time }}</div>
<div class="from">{{ item.from }}</div>
</div>
</div>
</div>
<div class="right-footer">
<div class="info">
{{ `共1059篇智库报告` }}
</div>
<div class="page-box">
<el-pagination :page-size="12" background layout="prev, pager, next" :total="120" />
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import Img1 from "./images/img1.png";
import Img2 from "./images/img2.png";
import Img3 from "./images/img3.png";
import Img4 from "./images/img4.png";
import Img5 from "./images/img5.png";
import Img6 from "./images/img6.png";
import Img7 from "./images/img7.png";
import Img8 from "./images/img8.png";
import Img9 from "./images/img9.png";
import Img10 from "./images/img10.png";
import Img11 from "./images/img11.png";
import Img12 from "./images/img12.png";
const activeFilters = reactive({
regions: ["north_america"],
types: ["gov"],
researches: ["全部领域"]
});
const filters = reactive({
regions: [
{ id: "north_america", name: "北美" },
{ id: "europe", name: "欧洲" },
{ id: "asia", name: "亚洲" },
{ id: "other", name: "其他地区" }
],
types: [
{ id: "gov", name: "政府背景" },
{ id: "university", name: "高校附属" },
{ id: "independent", name: "独立智库" },
{ id: "corporate", name: "企业视景" }
],
researches: [
{
id: "全部领域",
name: "全部领域"
},
{
id: "人工智能",
name: "人工智能"
},
{
id: "通信网络",
name: "通信网络"
},
{
id: "量子科技",
name: "量子科技"
},
{
id: "能源",
name: "能源"
},
{
id: "生物技术",
name: "生物技术"
},
{
id: "新材料",
name: "新材料"
},
{
id: "海洋",
name: "海洋"
}
]
});
const curFooterList = ref([
{
title: "中国对AI的转型产业政策",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img1
},
{
title: "中美对抗、竞争和合作跨越人工智能通用领域...",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img2
},
{
title: "中国、智慧城市和中东:地区和美国的选择",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img3
},
{
title: "中国对AI的转型产业政策",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img4
},
{
title: "中美经济竞争:复杂经济和地缘政治关系中的...",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img5
},
{
title: "中国、智慧城市和中东:留给地区和美国的选择",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img6
},
{
title: "中国对AI的转型产业政策",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img7
},
{
title: "中美对抗、竞争和合作跨越人工智能通用领域...",
time: "2025年6月26日",
from: "",
img: Img8
},
{
title: "中国、智慧城市和中东:地区和美国的选择",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img9
},
{
title: "中国对AI的转型产业政策",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img10
},
{
title: "中美对抗、竞争和合作跨越人工智能通用领域...",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img11
},
{
title: "中国、智慧城市和中东:地区和美国的选择",
time: "2025年6月26日",
from: "兰德科技智库",
img: Img12
}
]);
const yearList = ref([
{
label: "近一年发布",
value: "近一年发布"
},
{
label: "近两年发布",
value: "近两年发布"
},
{
label: "近三年发布",
value: "近三年发布"
}
]);
const selectedYear = ref("近一年发布");
</script>
<style lang="scss" scoped>
.main-header {
height: 64px;
display: flex;
justify-content: space-between;
.search-box {
margin-top: 16px;
display: flex;
width: 300px;
height: 32px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
.icon {
width: 16px;
height: 16px;
margin: 8px 7px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
.select-box {
margin-top: 16px;
margin-right: 5px;
}
}
.main-content {
display: flex;
gap: 16px;
.left {
width: 300px;
height: 806px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(94, 95, 95, 0.1);
background: rgba(255, 255, 255, 1);
position: relative;
.select-box {
margin-top: 21px;
.select-box-header {
display: flex;
gap: 17px;
.icon {
margin-top: 4px;
width: 8px;
height: 16px;
background: var(--color-main-active);
border-radius: 0 4px 4px 0;
}
.title {
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
}
.select-main {
margin-left: 25px;
}
.select-main1 {
width: 100px;
}
}
}
.right {
width: 1284px;
height: 1377px;
.card-box {
width: 1284px;
height: 1248px;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
.footer-card {
width: 418px;
height: 300px;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.footer-card-top {
width: 384px;
height: 206px;
margin: 0 auto;
margin-top: 15px;
img {
width: 100%;
height: 100%;
}
}
.footer-card-title {
margin: 0 auto;
margin-top: 13px;
width: 376px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 18px;
font-weight: 700;
line-height: 24px;
}
.footer-card-footer {
margin: 0 auto;
margin-top: 5px;
width: 376px;
height: 22px;
display: flex;
justify-content: space-between;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
.right-footer {
margin-top: 43px;
display: flex;
justify-content: space-between;
.info {
height: 19px;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="wrap">
<div class="left">
<div class="left-top">
<div class="left-box-header">
<div class="icon"></div>
<div class="title">{{ "基本信息" }}</div>
</div>
<div class="left-top-main">
<div class="img-box">
<img :src="basicInfo.img" alt="" />
</div>
<div class="left-top-item">
<div class="item-left">{{ "国家:" }}</div>
<div class="item-right">{{ basicInfo.country }}</div>
</div>
<div class="left-top-item">
<div class="item-left">{{ "成立时间:" }}</div>
<div class="item-right">{{ basicInfo.time }}</div>
</div>
<div class="left-top-item">
<div class="item-left">{{ "总部位置:" }}</div>
<div class="item-right">{{ basicInfo.position }}</div>
</div>
<div class="left-top-item">
<div class="item-left">{{ "机构性质:" }}</div>
<div class="item-right">{{ basicInfo.type }}</div>
</div>
<div class="left-top-item">
<div class="item-left">{{ "员工数量:" }}</div>
<div class="item-right">{{ basicInfo.num }}</div>
</div>
<div class="left-top-item">
<div class="item-left">{{ "年度预算:" }}</div>
<div class="item-right">{{ basicInfo.money }}</div>
</div>
</div>
</div>
<div class="left-bottom">
<div class="left-box-header">
<div class="icon"></div>
<div class="title">{{ "全球分支机构" }}</div>
</div>
<div class="left-bottom-main">
<div class="left-bottom-item">
<div class="item-left">{{ "北美:" }}</div>
<div class="item-right">{{ worldInfo.position1 }}</div>
</div>
<div class="left-bottom-item">
<div class="item-left">{{ "欧洲:" }}</div>
<div class="item-right">{{ worldInfo.position2 }}</div>
</div>
<div class="left-bottom-item">
<div class="item-left">{{ "中东:" }}</div>
<div class="item-right">{{ worldInfo.position3 }}</div>
</div>
<div class="left-bottom-item">
<div class="item-left">{{ "澳大利亚:" }}</div>
<div class="item-right">{{ worldInfo.position4 }}</div>
</div>
</div>
</div>
</div>
<div class="right">
<div class="box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">经费来源</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box1-main">
<div class="box1-main-left">
<div class="card1">
<div class="card-title">{{ "总计" }}</div>
<div class="card-num">{{ box1LeftData.total }}</div>
</div>
<div class="card2">
<div class="card-title">{{ "政府部门" }}</div>
<div class="card-num">{{ box1LeftData.gov }}</div>
</div>
<div class="card3">
<div class="card-title">{{ "其他机构" }}</div>
<div class="card-num">{{ box1LeftData.other }}</div>
</div>
</div>
<div class="box1-main-right" id="box1Chart"></div>
</div>
</div>
<div class="box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">研究领域演变</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box2-main">
<div class="box2-arrow-left">
<img src="@/assets/images/icon/card-btn-left.png" alt="" />
</div>
<div class="box2-arrow-right">
<img src="@/assets/images/icon/card-btn-right.png" alt="" />
</div>
<div class="box2-line-box"></div>
<div
class="box2-item"
:class="{ box2ItemFooter: index % 2 }"
v-for="(item, index) in timeLineList"
:key="index"
>
<div class="point" :class="{ pointFooter: index % 2 }">
<img src="./images/point.png" alt="" />
</div>
<div class="box2-item-header">
<div class="title">{{ item.time }}</div>
</div>
<div class="box2-item-content">
{{ item.content }}
</div>
</div>
</div>
</div>
<div class="box">
<div class="box-header">
<div class="header-left"></div>
<div class="title">核心研究人员</div>
<div class="header-right">
<div class="icon">
<img src="@/assets/icons/box-header-icon1.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon2.png" alt="" />
</div>
<div class="icon">
<img src="@/assets/icons/box-header-icon3.png" alt="" />
</div>
</div>
</div>
<div class="box3-main">
<div class="box3-main-left" id="box3Chart"></div>
<div class="box3-main-right">
<div class="box3-right-item" v-for="(item, index) in box3RightData" :key="index">
<div class="icon">
<img :src="item.img" alt="" />
</div>
<div class="info">
<div class="info-header">
<div class="name">{{ item.name }}</div>
<div class="position">{{ item.postion }}</div>
</div>
<div class="info-footer">{{ item.desc }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import setChart from "@/utils/setChart";
import getPieChart from "./utils/piechart";
import getTreeMapChart from "./utils/treeMapChart";
import InfoImg from "./images/img.png";
import Icon1 from "./images/icon1.png";
import Icon2 from "./images/icon2.png";
import Icon3 from "./images/icon3.png";
import Icon4 from "./images/icon4.png";
import Icon5 from "./images/icon5.png";
const basicInfo = ref({
img: InfoImg,
country: "美国",
time: "1948年",
position: "美国加利福尼亚州圣莫尼卡 ",
type: "非营利性研究与分析机构 ",
num: "约1,700名员工 ",
money: "约3.5亿美元 "
});
const worldInfo = ref({
position1: "圣莫尼卡(总部)、华盛顿特区、匹兹堡、波士顿",
position2: "英国剑桥、比利时布鲁塞尔",
position3: "卡塔尔多哈",
position4: "堪培拉 "
});
const box1LeftData = ref({
total: "4.358亿美元",
gov: "3.271亿美元",
other: "1.087亿美元"
});
const box1ChartData = ref([
{
name: "半导体",
value: 50,
color: "#4096FF"
},
{
name: "电子设备",
value: 46,
color: "#FFA39E"
},
{
name: "显示技术",
value: 40,
color: "#ADC6FF"
},
{
name: "新能源",
value: 32,
color: "#FFC069"
},
{
name: "通信设备",
value: 31,
color: "#B5F5EC"
},
{
name: "汽车",
value: 30,
color: "#B37FEB"
},
{
name: "其他",
value: 24,
color: "#D6E4FF"
}
]);
const timeLineList = ref([
{
time: "2025年7月25日",
content: "商务部已成立AI出口计划办公室,并开始招募专业人员。"
},
{
time: "2025年7月31日",
content: "英伟达、微软、谷歌等企业已提交初步技术栈提案。"
},
{
time: "2025年8月5日",
content: "国务院开始与盟友国家进行初步磋商。"
},
{
time: "2025年8月9日",
content: "国防部、能源部安全审查流程尚未最终确定。"
},
{
time: "2025年8月12日",
content: "商务部已成立AI出口计划办公室,并开始招募专业人员。"
}
]);
const box3RightData = ref([
{
img: Icon1,
name: "杰森·马西尼 ​",
postion: "兰德公司总裁兼首席执行官",
desc: "曾任职于哈佛大学经济学院、美国财政部"
},
{
img: Icon2,
name: "安德鲁·R·霍恩 ​",
postion: "高级副总裁,研究与分析",
desc: "曾任职于白宫科技政策顾问办公室"
},
{
img: Icon3,
name: "杰森·马西尼 ​",
postion: "兰德公司总裁兼首席执行官",
desc: "日籍研究院,毕业于斯坦福大学"
},
{
img: Icon4,
name: "梅丽莎·罗 ​",
postion: "副总裁,全球研究人才",
desc: "曾任职于美国国防部"
},
{
img: Icon5,
name: "安妮塔·钱德拉 ​ ​",
postion: "副总裁兼主任,兰德社会和经济福祉",
desc: "曾任职于哈佛大学经济学院、美国商务部"
}
]);
const handleBox3Chart = () => {
// 将分类数据转换为树状图格式
const treemapData = [];
const mockRandResearcherCategories = {
政府部门及国家实验室: {
商务部: 12,
财政部: 8,
能源部: 15,
国家能源技术实验室: 6,
其他: 10
},
领先科技企业: {
谷歌: 18,
微软: 14,
英伟达: 9,
英特尔: 11,
亚马逊: 13,
其他: 7
},
顶尖大学与研究机构: {
哈佛大学: 22,
加州大学: 19,
斯坦福大学: 16,
麻省理工学院: 14,
其他: 12
},
国际人才: {
印度: 15,
日本: 12,
德国: 10,
中国: 8,
其他: 6
}
};
Object.keys(mockRandResearcherCategories).forEach(category => {
const children = Object.keys(mockRandResearcherCategories[category]).map(item => ({
name: item,
value: mockRandResearcherCategories[category][item]
}));
treemapData.push({
name: category,
value: children.reduce((sum, item) => sum + item.value, 0),
children: children
});
});
const box3Chart = getTreeMapChart(treemapData);
setChart(box3Chart, "box3Chart");
};
onMounted(() => {
const box1Chart = getPieChart(box1ChartData.value);
setChart(box1Chart, "box1Chart");
handleBox3Chart();
});
</script>
<style lang="scss" scoped>
.wrap {
width: 100%;
height: 100%;
display: flex;
gap: 16px;
.left {
margin-top: 16px;
width: 480px;
height: 796px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.left-box-header {
display: flex;
.icon {
margin-top: 5px;
width: 8px;
height: 16px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
}
.left-top {
margin-top: 14px;
height: 515px;
.img-box {
width: 437px;
height: 204px;
margin: 0 auto;
margin-top: 16px;
margin-bottom: 22px;
img {
width: 100%;
height: 100%;
}
}
.left-top-item {
display: flex;
margin-top: 12px;
width: 332px;
height: 24px;
margin-left: 34px;
.item-left {
width: 88px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
.item-right {
width: 240px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
.left-bottom {
.left-bottom-main {
margin-top: 4px;
.left-bottom-item {
display: flex;
width: 354px;
margin-left: 34px;
margin-top: 12px;
.item-left {
width: 88px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
}
.item-right {
margin-left: 4px;
width: 240px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
}
.right {
width: 1104px;
height: 1245px;
.box {
width: 1104px;
height: 390px;
margin-top: 16px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
.box-header {
height: 56px;
display: flex;
position: relative;
.header-left {
margin-top: 20px;
width: 8px;
height: 20px;
border-radius: 0 4px 4px 0;
background: var(--color-main-active);
}
.title {
margin-left: 14px;
margin-top: 20px;
height: 16px;
line-height: 16px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 16px;
}
.header-right {
position: absolute;
top: 14px;
right: 12px;
display: flex;
justify-content: flex-end;
gap: 4px;
.icon {
width: 28px;
height: 28px;
img {
width: 100%;
height: 100%;
}
}
}
}
.box1-main {
display: flex;
.box1-main-left {
width: 156px;
margin-left: 41px;
margin-top: 26px;
.card1 {
width: 156px;
height: 72px;
border-radius: 4px;
background: linear-gradient(180deg, rgba(230, 244, 255, 1), rgba(230, 244, 255, 0) 100%);
.card-title {
height: 24px;
margin-top: 8px;
margin-left: 19px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.card-num {
margin-top: 5px;
margin-left: 19px;
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
.card2 {
margin-top: 20px;
width: 156px;
height: 72px;
border-radius: 4px;
background: linear-gradient(180deg, rgba(255, 241, 240, 1), rgba(255, 241, 240, 0) 100%);
.card-title {
height: 24px;
margin-top: 8px;
margin-left: 19px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.card-num {
margin-top: 5px;
margin-left: 19px;
height: 24px;
color: rgba(206, 79, 81, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
.card3 {
margin-top: 20px;
width: 156px;
height: 72px;
border-radius: 4px;
background: linear-gradient(180deg, rgba(230, 255, 251, 1), rgba(230, 255, 251, 0) 100%);
.card-title {
height: 24px;
margin-top: 8px;
margin-left: 19px;
color: rgba(19, 168, 168, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.card-num {
margin-top: 5px;
margin-left: 19px;
height: 24px;
color: rgba(19, 168, 168, 1);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
.box1-main-right {
margin-left: 38px;
margin-top: 26px;
width: 800px;
height: 270px;
}
}
.box2-main {
height: 320px;
display: flex;
position: relative;
padding-left: 150px;
.box2-arrow-left {
width: 24px;
height: 48px;
position: absolute;
left: 0;
top: 130px;
z-index: 999;
img {
width: 100%;
height: 100%;
}
}
.box2-arrow-right {
width: 24px;
height: 48px;
position: absolute;
right: 0;
top: 130px;
z-index: 999;
img {
width: 100%;
height: 100%;
}
}
.box2-line-box {
position: absolute;
left: 0;
top: 150px;
height: 8px;
width: 1100px;
background: url("./images/line-bg.png") repeat;
}
.box2-item {
margin-top: 4px;
width: 300px;
height: 150px;
position: relative;
box-sizing: border-box;
padding-left: 13px;
margin-left: -100px;
border-left: 1px solid #0a57a6;
.box2-item-header {
display: flex;
width: 240px;
.title {
color: rgba(10, 87, 166, 1);
height: 26px;
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
}
}
.box2-item-content {
width: 210px;
min-height: 48px;
max-height: 96px;
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;
line-height: 24px;
overflow: hidden;
}
.point {
position: absolute;
left: -8px;
bottom: -7px;
width: 15px;
height: 15px;
img {
width: 100%;
height: 100%;
}
}
.pointFooter {
position: absolute;
left: -8px;
top: -7px;
width: 15px;
height: 15px;
img {
width: 100%;
height: 100%;
}
}
.time {
height: 24px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
text-align: justify;
position: absolute;
bottom: -36px;
left: 0;
}
}
.box2ItemFooter {
margin-top: 154px;
margin-left: -100px;
box-sizing: border-box;
padding-top: 20px;
}
}
.box3-main {
display: flex;
.box3-main-left {
width: 536px;
height: 326px;
margin-left: 10px;
}
.box3-main-right {
margin-left: 10px;
width: 536px;
height: 326px;
.box3-right-item {
display: flex;
width: 520px;
height: 64px;
align-items: center;
.icon {
width: 48px;
height: 48px;
margin-left: 8px;
img {
width: 100%;
height: 100%;
}
}
.info {
width: 459px;
margin-left: 13px;
.info-header {
display: flex;
justify-content: space-between;
.name {
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
letter-spacing: 1px;
}
.position {
height: 22px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
letter-spacing: 0px;
text-align: right;
}
}
.info-footer {
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: left;
}
}
}
}
}
}
}
}
</style>
\ No newline at end of file
const getPieChart = (data) => {
let option = {
series: [
{
type: 'pie',
radius: [70, 100],
height: '100%',
left: 'center',
width: '100%',
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
label: {
alignTo: 'edge',
formatter: '{name|{b}}\n{time|{c} 条 {d}%}',
minMargin: 15,
edgeDistance: 10,
lineHeight: 15,
rich: {
name: {
fontSize: 16,
color: 'rgba(59, 65, 75, 1)',
fontWeight: 700,
},
time: {
fontSize: 14,
color: 'rgba(59, 65, 75, 1)',
}
}
},
labelLine: {
length: 15,
length2: 0,
maxSurfaceAngle: 80
},
labelLayout: function (params) {
const isLeft = params.labelRect.x < 556 / 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: data
}]
}
return option
}
export default getPieChart;
\ No newline at end of file
const getTreeMapChart = (treemapData) => {
const option = {
tooltip: {
trigger: 'item',
formatter: '{b}: {c}人'
},
grid: {
top: '2%',
left: '2%',
right: '2%',
bottom: '2%',
containLabel: true
},
series: [
{
type: 'treemap',
data: treemapData,
roam: false,
nodeClick: false,
breadcrumb: {
show: false
},
label: {
show: true,
formatter: '{b}\n{c}人',
fontSize: 12
},
upperLabel: {
show: true,
height: 30
},
itemStyle: {
borderColor: '#fff',
borderWidth: 2,
gapWidth: 2
}
}
]
}
return option
}
export default getTreeMapChart
\ No newline at end of file
......@@ -18,7 +18,7 @@
<div class="search-text">搜索</div>
</div>
</div>
<div class="home-main-header-footer">
<!-- <div class="home-main-header-footer">
<div class="home-main-header-footer-item">
<div class="item-top">128</div>
<div class="item-footer">追踪智库数量</div>
......@@ -35,7 +35,7 @@
<div class="item-top">12</div>
<div class="item-footer">热点科技领域</div>
</div>
</div>
</div> -->
<div class="home-main-header-btn-box">
<div class="btn" @click="scrollToTop('position1')">
<div class="btn-text">{{ "最新动态" }}</div>
......@@ -63,7 +63,7 @@
</div>
</div>
<div class="home-main-header-card-box">
<div class="card" v-for="(item, index) in cardList" :key="index">
<div class="card" v-for="(item, index) in cardList" :key="index" @click="handleClick(item)">
<div class="card-header">
<div class="icon">
<img :src="item.logo" alt="" />
......@@ -450,6 +450,12 @@ import Img10 from "./assets/images/img10.png";
import Img11 from "./assets/images/img11.png";
import Img12 from "./assets/images/img12.png";
const handleClick = tank => {
// router.push({ name: "ThinkTankDetail", params: { id: tank.id } });
const curRoute = router.resolve({ name: "ThinkTankDetail", params: { id: tank.id } });
window.open(curRoute.href, "_blank");
};
const activeFilters = reactive({
regions: ["north_america"],
types: ["gov"],
......@@ -508,6 +514,7 @@ const filters = reactive({
const cardList = ref([
{
id: 1,
logo: Logo1,
rank: 1,
name: "兰德科技智库",
......@@ -525,6 +532,7 @@ const cardList = ref([
]
},
{
id: 2,
logo: Logo2,
rank: 2,
name: "布鲁金斯学会",
......@@ -542,6 +550,7 @@ const cardList = ref([
]
},
{
id: 3,
logo: Logo3,
rank: 3,
name: "麦肯锡全球研究院",
......@@ -559,6 +568,7 @@ const cardList = ref([
]
},
{
id: 4,
logo: Logo4,
rank: 4,
name: "麻省理工学院科技评论",
......@@ -576,6 +586,7 @@ const cardList = ref([
]
},
{
id: 5,
logo: Logo5,
rank: 5,
name: "信息技术与创新基金会",
......@@ -704,8 +715,8 @@ const activeHylyId = ref("");
const handleClickCate = cate => {
console.log(cate);
activeCate.value = cate.hylymc;
activeHylyId.value = cate.hylyid;
activeCate.value = cate;
activeHylyId.value = cate;
handleGetBillsByType();
};
......@@ -988,7 +999,7 @@ onMounted(async () => {
chart1Data.value.data[1].value,
chart1Data.value.data[2].value
);
setChart(box5Chart, 'box5Chart')
setChart(box5Chart, "box5Chart");
const box6Chart = getPieChart(box6Data.value);
setChart(box6Chart, "box6Chart");
......@@ -1149,7 +1160,7 @@ onMounted(async () => {
right: 19px;
width: 6px;
height: 12px;
img{
img {
width: 100%;
height: 100%;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论