提交 b0c9e35c authored 作者: coderBryanFu's avatar coderBryanFu

Merge branch 'pre' of http://8.140.26.4:10003/caijian/risk-monitor into fk-dev

流水线 #214 已通过 于阶段
in 1 分 43 秒
...@@ -5,12 +5,29 @@ ...@@ -5,12 +5,29 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { useRoute } from "vue-router";
import AiBox from "./components/AiBox.vue";
import { getPersonType } from "@/api/common/index"; import { getPersonType } from "@/api/common/index";
// import { useDraggable } from "@vueuse/core";
import Menu1 from "@/assets/icons/overview/menu1.png";
import Menu2 from "@/assets/icons/overview/menu2.png";
import Menu3 from "@/assets/icons/overview/menu3.png";
import Menu4 from "@/assets/icons/overview/menu4.png";
import Menu5 from "@/assets/icons/overview/menu5.png";
import Menu6 from "@/assets/icons/overview/menu6.png";
import Menu7 from "@/assets/icons/overview/menu7.png";
import Menu8 from "@/assets/icons/overview/menu8.png";
import Menu9 from "@/assets/icons/overview/menu9.png";
import Menu10 from "@/assets/icons/overview/menu10.png";
import Menu11 from "@/assets/icons/overview/menu11.png";
import Menu12 from "@/assets/icons/overview/menu12.png";
import { ElMessage } from "element-plus";
const router = useRouter(); const router = useRouter();
const route = useRoute();
import useTagsViewStore from '@/stores/tagsView.js' import useTagsViewStore from '@/stores/tagsView.js'
...@@ -20,7 +37,7 @@ const tagsViewStore = useTagsViewStore() ...@@ -20,7 +37,7 @@ const tagsViewStore = useTagsViewStore()
// 在路由全局守卫中处理 // 在路由全局守卫中处理
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 路由允许添加标签(排除掉隐藏的布局页如 /404, /login 等) // 路由允许添加标签(排除掉隐藏的布局页如 /404, /login 等)
if (to.path.indexOf('dataLibrary') > -1 ) { if (to.path.includes('dataLibrary')) {
tagsViewStore.addView({ tagsViewStore.addView({
path: to.path, path: to.path,
name: to.name, // 对应组件的 name,用于缓存 name: to.name, // 对应组件的 name,用于缓存
...@@ -31,6 +48,17 @@ router.beforeEach((to, from, next) => { ...@@ -31,6 +48,17 @@ router.beforeEach((to, from, next) => {
next() next()
}) })
const isShowAiBox = ref(false);
const closeAiBox = () => {
isShowAiBox.value = false;
};
const openAiBox = () => {
isShowAiBox.value = true;
};
const personTypeList = ref([]); const personTypeList = ref([]);
// 获取人物类别 // 获取人物类别
...@@ -47,6 +75,146 @@ const handleGetPersonType = async () => { ...@@ -47,6 +75,146 @@ const handleGetPersonType = async () => {
} catch (error) {} } catch (error) {}
}; };
const isCurrentOverview = computed(() => {
if (route.path === "/ZMOverView") {
return true;
} else {
return false;
}
});
// 概览页标题列表
const homeTitleList = ref([
{
name: "中美科技博弈",
path: "/ZMOverView",
disabled: false
},
{
name: "主要国家科技动向感知",
path: "",
disabled: true
},
{
name: "主要国家竞争科技安全",
path: "",
disabled: true
}
]);
const homeActiveTitleIndex = ref(0);
const isShowMenu = ref(false);
const handleShowMenu = (index, isShow) => {
if (index === 0) {
isShowMenu.value = isShow;
}
};
const handleHoverMenu = isShow => {
isShowMenu.value = isShow;
};
const menuList = ref([
{
title: "中美科技博弈概览",
icon: Menu1,
path: "/ZMOverView"
},
{
title: "科技法案",
icon: Menu2,
path: "/billHome"
},
{
title: "科技政令",
icon: Menu3,
path: "/decree"
},
{
title: "美国科技智库",
icon: Menu4,
path: "/thinkTank"
},
{
title: "出口管制",
icon: Menu5,
path: "/exportControl"
},
{
title: "科研合作限制",
icon: Menu6,
path: "/cooperationRestrictions"
},
{
title: "投融资限制",
icon: Menu7,
path: "/finance"
},
{
title: "市场准入限制",
icon: Menu8,
path: "/marketAccessRestrictions"
},
{
title: "规则限制",
icon: Menu9,
path: "/ruleRestrictions"
},
{
title: "美国科技人物观点",
icon: Menu10,
path: "/technologyFigures"
},
{
title: "美国主要创新主体动向",
icon: Menu11,
path: "/innovationSubject"
},
{
title: "美国科研资助体系",
icon: Menu12,
path: "/scientificFunding"
}
]);
const handleToModule = item => {
const curRoute = router.resolve({
path: item.path
});
window.open(curRoute.href, "_blank");
};
const searchText = ref("");
const handleSearch = () => {
const curRoute = router.resolve({
path: "/searchResults",
query: {
searchText: searchText.value
}
});
window.open(curRoute.href, "_blank");
};
const handleClickTitle = item => {
if (item.name === "主要国家科技动向感知" || item.name === "主要国家竞争科技安全") {
ElMessage.warning("当前功能正在开发中,敬请期待!");
}
};
const handleOpenPage = page => {
const pageObj = {
znwd: "/chat",
znxb: "/writtingAsstaint"
};
window.open(pageObj[page], "_blank");
};
const handleClickToolBox = () => {
ElMessage.warning("当前功能正在开发中,敬请期待!");
};
onMounted(() => { onMounted(() => {
handleGetPersonType(); handleGetPersonType();
}); });
...@@ -113,6 +281,457 @@ body { ...@@ -113,6 +281,457 @@ body {
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
} }
.pro-wrapper {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
.home-page {
width: 100%;
height: 100%;
position: relative;
.navbar {
width: 100%;
height: 64px;
border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: linear-gradient(180deg, rgba(246, 250, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%);
.nav-content {
width: 1600px;
height: 64px;
margin: 0 auto;
display: flex;
justify-content: space-between;
position: relative;
.nav-left {
height: 64px;
display: flex;
.icon {
margin-top: 17px;
width: 29px;
height: 30px;
img {
width: 100%;
height: 100%;
}
}
.title-box {
display: flex;
height: 64px;
margin-left: 21px;
gap: 33px;
.title {
height: 64px;
cursor: pointer;
&:hover {
.text {
color: var(--color-main-active);
}
}
.text {
height: 39px;
margin-top: 12px;
color: rgba(59, 65, 75, 1);
font-family: YouSheBiaoTiHei;
font-style: Regular;
font-size: 30px;
font-weight: 400;
line-height: 39px;
letter-spacing: 0px;
}
.textActive {
color: var(--color-main-active);
}
.bottom-line {
width: 50px;
height: 4px;
margin: 0 auto;
margin-top: 9px;
background: var(--color-main-active);
}
}
}
}
.nav-right {
display: flex;
justify-content: flex-end;
gap: 21px;
.search-box {
margin-top: 16px;
width: 300px;
height: 36px;
box-sizing: border-box;
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 10px;
background: rgba(231, 243, 255, 1);
display: flex;
.input {
width: 264px;
height: 36px;
}
.icon {
width: 18px;
height: 18px;
margin-left: 9px;
margin-top: 9px;
img {
width: 100%;
height: 100%;
}
}
}
.info-box {
height: 64px;
display: flex;
justify-content: flex-end;
align-items: center;
.mail {
width: 32px;
height: 32px;
margin-right: 14px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.user {
width: 32px;
height: 32px;
margin-right: 11px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
.name {
width: 48px;
height: 30px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 400;
line-height: 30px;
letter-spacing: 0px;
}
}
}
.menu-box {
position: absolute;
z-index: 999999;
width: 713px;
height: 413px;
top: 64px;
left: 0;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
backdrop-filter: blur(30px);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.8);
.menu-content {
width: 562px;
height: 348px;
margin-top: 8px;
margin-left: 72px;
display: flex;
flex-wrap: wrap;
.menu-item {
margin-top: 36px;
width: 280px;
height: 24px;
display: flex;
cursor: pointer;
&:hover {
.title {
color: var(--color-main-active);
font-size: 20px;
}
}
.icon {
width: 24px;
height: 24px;
img {
width: 100%;
height: 100%;
}
}
.title {
margin-left: 16px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 18px;
font-weight: 700;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
}
}
.main-container {
width: 100%;
height: calc(100vh - 64px);
position: relative;
overflow: hidden;
}
}
.content-page {
width: 100%;
height: 100%;
.navbar {
display: flex;
justify-content: center;
align-items: center;
background: #fff;
color: rgba(10, 18, 30, 1);
border-bottom: 1px solid #e5e7eb;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
position: relative;
box-sizing: border-box;
height: 72px;
.nav-brand {
display: flex;
align-items: center;
gap: 12px;
position: absolute;
left: 160px;
.brand-icon {
width: 48px;
height: 48px;
img {
width: 100%;
height: 100%;
}
}
.brand-text {
cursor: pointer;
.text-ch {
height: 37px;
color: rgba(10, 18, 30, 1);
font-family: Microsoft YaHei;
font-size: 32px;
font-weight: 700;
line-height: 37px;
}
.text-en {
color: rgba(10, 18, 30, 1);
font-family: Microsoft YaHei;
font-size: 10px;
font-weight: 400;
line-height: 13px;
}
}
}
.user-info {
display: flex;
align-items: center;
gap: 20px;
padding: 8px 12px;
border-radius: 6px;
color: #333;
position: absolute;
right: 160px;
.email {
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.avator {
width: 32px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
.user {
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 30px;
}
}
}
.main-container {
width: 100%;
height: calc(100vh - 72px);
position: relative;
overflow: hidden;
}
}
.right-btn {
position: absolute;
// top: 132px;
top: 100px;
right: 0;
z-index: 10000000000000;
.item {
width: 108px;
height: 40px;
box-sizing: border-box;
border: 1px solid #fff;
border-radius: 50px 0px 0px 50px;
background: rgba(255, 255, 255, 0.65);
display: flex;
margin-bottom: 8px;
cursor: pointer;
.icon {
width: 36px;
height: 36px;
margin-top: 2px;
margin-left: 2px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 36px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Source Han Sans CN;
font-style: Regular;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
margin-top: 8px;
margin-left: 10px;
}
}
}
.tool-box {
position: absolute;
top: 400px;
right: 28px;
top: 681px;
width: 62px;
height: 217px;
box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 0.65);
box-sizing: border-box;
padding: 24px 18px;
display: flex;
flex-direction: column;
justify-content: space-between;
.tool-item {
width: 24px;
height: 24px;
cursor: pointer;
img {
width: 100%;
height: 100%;
}
}
}
.ai-btn {
position: absolute;
bottom: 240px;
right: 10px;
z-index: 9999;
cursor: pointer;
.icon {
width: 96px;
height: 96px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-top: -15px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
text-align: center;
}
}
.ai-dialog {
position: absolute;
right: 100px;
top: 100px;
z-index: 9999;
}
}
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
height: 100%; height: 100%;
box-shadow: none; box-shadow: none;
...@@ -128,4 +747,27 @@ body { ...@@ -128,4 +747,27 @@ body {
box-shadow: none !important; box-shadow: none !important;
} }
.title[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.info-box[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.right-btn[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.tool-box[aria-disabled="true"] {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
</style> </style>
...@@ -2,67 +2,65 @@ import request from "@/api/request.js"; ...@@ -2,67 +2,65 @@ import request from "@/api/request.js";
// 中美博弈概览V2:最新风险动态统计 // 中美博弈概览V2:最新风险动态统计
export function getLatestRiskUpdates(params) { export function getLatestRiskUpdates(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/rivalryIndexV2/LatestRiskUpdates`, url: `/api/rivalryIndexV2/LatestRiskUpdates`,
params: params params: params
}) });
} }
// 中美博弈概览V2:最新风险信号 // 中美博弈概览V2:最新风险信号
export function getLatestRisks() { export function getLatestRisks() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/rivalryIndexV2/LatestRisks`, url: `/api/rivalryIndexV2/LatestRisks`
});
})
} }
// 中美博弈概览V2:美对华制裁措施数量趋势 // 中美博弈概览V2:美对华制裁措施数量趋势
export function geDomainContainmentTrend(params) { export function geDomainContainmentTrend(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/rivalryIndexV2/DomainContainmentTrend`, url: `/api/rivalryIndexV2/DomainContainmentTrend`,
params: params params: params
}) });
} }
// 中美博弈概况:获取榜单字典 // 中美博弈概况:获取榜单字典
export function getChartDict() { export function getChartDict() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/summary/chartDict`, url: `/api/union/summary/chartDict`
}) });
}
// 中美博弈概况:获取年份
export function getYear(id) {
return request({
method: "GET",
url: `/api/union/summary/chartYear/${id}`
});
} }
// 中美博弈概况:中美科技实力对比 // 中美博弈概况:中美科技实力对比
export function getCompare(id) { export function getCompare(id, year) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/summary/compare/${id}`, url: `/api/union/summary/compare/${id}/${year}`
}) });
} }
// 中美博弈分析 // 中美博弈分析
export function getTechnologyGameAnalysis() { export function getTechnologyGameAnalysis() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/rivalryIndexV2/TechnologyGameAnalysis`, url: `/api/rivalryIndexV2/TechnologyGameAnalysis`
});
})
} }
//中美博弈概览V7:美国政府部门对华制裁最新动态 //中美博弈概览V7:美国政府部门对华制裁最新动态
export function getGovernmentSanctionsDynamics() { export function getGovernmentSanctionsDynamics() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/rivalryIndex/governmentSanctionsDynamics`, url: `/api/rivalryIndex/governmentSanctionsDynamics`
});
}) }
}
\ No newline at end of file
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: block;
} }
} }
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
const thinkTank = () => import('@/views/thinkTank/index.vue') const thinkTank = () => import('@/views/thinkTank/index.vue')
const ThinkTankDetail = () => import('@/views/thinkTank/ThinkTankDetail/index.vue') const ThinkTankDetail = () => import('@/views/thinkTank/ThinkTankDetail/index.vue')
const ReportDetail = () => import('@/views/thinkTank/ReportDetail/index.vue') const ReportDetail = () => import('@/views/thinkTank/ReportDetail/index.vue')
const SurveyProjectView = () => import('@/views/thinkTank/SurveyProjectView/index.vue')
const CongressHearingView = () => import('@/views/thinkTank/CongressHearingView/index.vue')
const ReportOriginal = () => import('@/views/thinkTank/reportOriginal/index.vue') const ReportOriginal = () => import('@/views/thinkTank/reportOriginal/index.vue')
const allThinkTank= () => import('@/views/thinkTank/allThinkTank/index.vue') const allThinkTank= () => import('@/views/thinkTank/allThinkTank/index.vue')
const MultiThinkTankViewAnalysis= () => import('@/views/thinkTank/MultiThinkTankViewAnalysis/index.vue') const MultiThinkTankViewAnalysis= () => import('@/views/thinkTank/MultiThinkTankViewAnalysis/index.vue')
...@@ -40,6 +42,16 @@ const thinktankRoutes = [ ...@@ -40,6 +42,16 @@ const thinktankRoutes = [
name: "ReportOriginal", name: "ReportOriginal",
component: ReportOriginal, component: ReportOriginal,
},
{
path: "/thinkTank/SurveyProjectView/:id",
name: "SurveyProjectView",
component: SurveyProjectView,
},
{
path: "/thinkTank/CongressHearingView/:id",
name: "CongressHearingView",
component: CongressHearingView,
}, },
{ {
path: "/thinkTank/allThinkTank", path: "/thinkTank/allThinkTank",
......
...@@ -14,10 +14,20 @@ ...@@ -14,10 +14,20 @@
size="default" size="default"
style="margin-left: 15px; width: 240px; height: 32px" style="margin-left: 15px; width: 240px; height: 32px"
v-model="origin" v-model="origin"
@change="handleGetCompare()" @change="handleOriginChange()"
> >
<el-option :value="value.id" :label="value.name" v-for="(value, index) in originList" :key="index" /> <el-option :value="value.id" :label="value.name" v-for="(value, index) in originList" :key="index" />
</el-select> </el-select>
<el-select
class="select-item"
size="default"
style="margin-left: 15px; width: 200px; height: 32px"
v-model="year"
@change="handleGetCompare()"
>
<el-option :value="value" :label="value" v-for="(value, index) in yearList" :key="index" />
</el-select>
</div> </div>
</div> </div>
<div style="display: flex; height: 650px; width: 100%; padding-top: 12px"> <div style="display: flex; height: 650px; width: 100%; padding-top: 12px">
...@@ -237,7 +247,7 @@ import Echarts from "@/components/Chart/index.vue"; ...@@ -237,7 +247,7 @@ import Echarts from "@/components/Chart/index.vue";
import mockData from "./mock.json"; import mockData from "./mock.json";
import radarChart from "./radarChart3.js"; import radarChart from "./radarChart3.js";
import { getCompare, getChartDict, getTechnologyGameAnalysis } from "@/api/zmOverview/risk/index.js"; import { getCompare, getChartDict, getYear, getTechnologyGameAnalysis } from "@/api/zmOverview/risk/index.js";
import icon1 from "./icon/btn-icon-0.png"; import icon1 from "./icon/btn-icon-0.png";
import icon2 from "./icon/btn-icon-1.png"; import icon2 from "./icon/btn-icon-1.png";
import icon3 from "./icon/btn-icon-2.png"; import icon3 from "./icon/btn-icon-2.png";
...@@ -351,10 +361,32 @@ const handleGetChartDict = async () => { ...@@ -351,10 +361,32 @@ const handleGetChartDict = async () => {
console.error("获取数据来源error", error); console.error("获取数据来源error", error);
} }
}; };
const yearList = ref([]);
const year = ref("");
//年份
const handleGetYear = async () => {
try {
const res = await getYear(origin.value);
console.log("年份", res);
if (res.code === 200 && res.data) {
yearList.value = res.data;
year.value = res.data[0];
}
} catch (error) {
console.error("获取年份error", error);
}
};
// 数据来源改变时,年份列表也会改变
const handleOriginChange = async () => {
await handleGetYear();
};
//中美科技实力对比 //中美科技实力对比
const handleGetCompare = async () => { const handleGetCompare = async () => {
try { try {
const res = await getCompare(origin.value); const res = await getCompare(origin.value, year.value);
console.log("中美科技实力对比", res); console.log("中美科技实力对比", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
tableData.value = res.data[0].children; tableData.value = res.data[0].children;
...@@ -392,6 +424,7 @@ const handlegetTechnologyGameAnalysis = async () => { ...@@ -392,6 +424,7 @@ const handlegetTechnologyGameAnalysis = async () => {
}; };
onMounted(async () => { onMounted(async () => {
await handleGetChartDict(); await handleGetChartDict();
await handleGetYear();
await handleGetCompare(); await handleGetCompare();
await handlegetTechnologyGameAnalysis(); await handlegetTechnologyGameAnalysis();
// const dom = document.getElementById("char"); // const dom = document.getElementById("char");
...@@ -463,7 +496,13 @@ const handleIndicatorChange = indicator => { ...@@ -463,7 +496,13 @@ const handleIndicatorChange = indicator => {
const years = chinaData.map(item => item.year.toString()); const years = chinaData.map(item => item.year.toString());
const chinaValues = chinaData.map(item => item.value); const chinaValues = chinaData.map(item => item.value);
const usaValues = usaData.map(item => item.value); const usaValues = usaData.map(item => item.value);
console.log("中国", chinaValues);
console.log("美国", usaValues);
// chinaValues和usaValues的最小值和最大值
const min = Math.min(...chinaValues, ...usaValues);
const max = Math.max(...chinaValues, ...usaValues);
lineOption.value.yAxis.min = Math.floor(min - 1);
lineOption.value.yAxis.max = Math.ceil(max + 1);
lineOption.value.xAxis.data = years; lineOption.value.xAxis.data = years;
lineOption.value.series = [ lineOption.value.series = [
{ {
...@@ -566,9 +605,11 @@ const lineOption = ref({ ...@@ -566,9 +605,11 @@ const lineOption = ref({
}, },
yAxis: { yAxis: {
type: "value", type: "value",
// name: "指数", min: 77,
nameLocation: "top", name: "指数",
nameGap: 35, nameLocation: "end",
nameGap: 1,
padding: [0, -50, 0, 0],
nameTextStyle: { nameTextStyle: {
color: "#666", color: "#666",
fontSize: 13, fontSize: 13,
...@@ -589,7 +630,7 @@ const lineOption = ref({ ...@@ -589,7 +630,7 @@ const lineOption = ref({
} }
}, },
axisLabel: { axisLabel: {
color: "#ccc", color: "#666",
fontSize: 14, fontSize: 14,
fontWeight: 400, fontWeight: 400,
formatter: value => { formatter: value => {
......
...@@ -25,8 +25,8 @@ ...@@ -25,8 +25,8 @@
</div> </div>
</div> </div>
</div> --> </div> -->
<NewsList :newsList="leftList" @item-click="handleToNewsDetail" @more-click="handleToMoreNews" img="image" <NewsList :newsList="leftList" @item-click="item => gotoNewsDetail(item.id)" @more-click="handleToMoreNews"
title="title" content="content" from="from" /> img="image" title="title" content="content" from="from" />
<MessageBubble :messageList="rightList" imageUrl="personImage" @more-click="handleToSocialDetail" <MessageBubble :messageList="rightList" imageUrl="personImage" @more-click="handleToSocialDetail"
@person-click="handleToSocialDetail" name="name" content="content" source="orgName" image-url="image" /> @person-click="handleToSocialDetail" name="name" content="content" source="orgName" image-url="image" />
<!-- <div class="right"> <!-- <div class="right">
...@@ -60,6 +60,8 @@ import title03 from './assets/title03.png' ...@@ -60,6 +60,8 @@ import title03 from './assets/title03.png'
import title01bg from './assets/title01bg.png' import title01bg from './assets/title01bg.png'
import title02bg from './assets/title02bg.png' import title02bg from './assets/title02bg.png'
import title03bg from './assets/title03bg.png' import title03bg from './assets/title03bg.png'
import { useGotoNewsDetail } from '@/router/modules/news';
const gotoNewsDetail = useGotoNewsDetail()
// 合作限制-查询社交媒体接口 // 合作限制-查询社交媒体接口
const getCoopRestrictionSocialData = async () => { const getCoopRestrictionSocialData = async () => {
......
...@@ -12,50 +12,52 @@ ...@@ -12,50 +12,52 @@
<el-carousel ref="carouselRef" height="412px" direction="horizontal" :autoplay="true" :interval="5000" <el-carousel ref="carouselRef" height="412px" direction="horizontal" :autoplay="true" :interval="5000"
arrow="never" indicator-position="none" @change="handleCarouselChange"> arrow="never" indicator-position="none" @change="handleCarouselChange">
<el-carousel-item v-for="(item, index) in coopRestrictionTrends" :key="item.ID || index"> <el-carousel-item v-for="(item, index) in coopRestrictionTrends" :key="item.ID || index">
<div class="carousel-item-content">
<div class="left-center"> <div class="left-center">
<img :src="item.IMAGEURL || defaultImg" alt="" /> <img :src="item.IMAGEURL || defaultImg" alt="" />
<div class="left-center-main"> <div class="left-center-main">
<div class="left-center-main-title">{{ item.LIMITNAME || "暂无动态" }}</div> <div class="left-center-main-title">{{ item.LIMITNAME || "暂无动态" }}</div>
<div class="left-center-main-ul"> <div class="left-center-main-ul">
<ul> <ul>
<li> <li>
<span class="ul-title">数据来源:</span> <span class="ul-title">数据来源:</span>
<span class="ul-content">{{ item.ORGNAME || "未知" }}</span> <span class="ul-content">{{ item.ORGNAME || "未知" }}</span>
</li> </li>
<li> <li>
<span class="ul-title">合作限制类型:</span> <span class="ul-title">合作限制类型:</span>
<span class="ul-content">{{ item.LIMITTYPE || "未知" }}</span> <span class="ul-content">{{ item.LIMITTYPE || "未知" }}</span>
</li> </li>
<li> <li>
<span class="ul-title">发布日期:</span> <span class="ul-title">发布日期:</span>
<span class="ul-content">{{ item.LIMITDATE || "未知" }}</span> <span class="ul-content">{{ item.LIMITDATE || "未知" }}</span>
</li> </li>
<li> <li>
<span class="ul-title">涉及领域:</span> <span class="ul-title">涉及领域:</span>
<div class="ul-tags" v-if="item.AREA"> <div class="ul-tags" v-if="item.AREA">
<span v-for="(field, fIndex) in typeof item.AREA === 'string' <span v-for="(field, fIndex) in typeof item.AREA === 'string'
? item.AREA.split(',') ? item.AREA.split(',')
: item.AREA" :key="fIndex" class="ul-pie" :class="'cl' + ((fIndex % 3) + 1)"> : item.AREA" :key="fIndex" class="ul-pie" :class="'cl' + ((fIndex % 3) + 1)">
{{ field }} {{ field }}
</span> </span>
</div> </div>
<span v-else class="ul-content">未知</span> <span v-else class="ul-content">未知</span>
</li> </li>
</ul> </ul>
</div>
</div> </div>
<!-- <div class="left-center-title">{{ item.LIMITTYPE }}</div> -->
</div> </div>
<div class="left-bottom"> <div class="left-center-type" v-if="item.type">{{ item.type }}</div>
<ul> <!-- <div class="left-center-title">{{ item.LIMITTYPE }}</div> -->
<li class="left-bottom-li">内容摘要:</li> </div>
</ul> <div class="left-bottom">
<div class="left-bottom-content"> <ul>
{{ item.INTRODUCTION || "暂无内容摘要" }} <li class="left-bottom-li">内容摘要:</li>
</div> </ul>
<div class="left-bottom-content">
{{ item.INTRODUCTION || "暂无内容摘要" }}
</div> </div>
</div> </div>
</el-carousel-item> </el-carousel-item>
<!-- 无数据时的占位展示 --> <!-- 无数据时的占位展示 -->
...@@ -108,6 +110,7 @@ ...@@ -108,6 +110,7 @@
查看更多 查看更多
</div> </div>
</div> --> </div> -->
<RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content" <RiskSignal :list="riskSignals" @more-click="handleToMoreRiskSignal" postDate="time" name="content"
riskLevel="title" @item-click="handleClickToDetail" /> riskLevel="title" @item-click="handleClickToDetail" />
</div> </div>
...@@ -233,7 +236,7 @@ onMounted(() => { ...@@ -233,7 +236,7 @@ onMounted(() => {
.left { .left {
width: 1064px; width: 1064px;
height: 460px; height: 450px;
margin-right: 16px; margin-right: 16px;
border-radius: 10px; border-radius: 10px;
background-color: #fff; background-color: #fff;
...@@ -330,6 +333,7 @@ onMounted(() => { ...@@ -330,6 +333,7 @@ onMounted(() => {
.left-center-main { .left-center-main {
width: 439px; width: 439px;
height: 175px; height: 175px;
position: relative;
.left-center-main-title { .left-center-main-title {
margin-left: 19px; margin-left: 19px;
...@@ -420,6 +424,29 @@ onMounted(() => { ...@@ -420,6 +424,29 @@ onMounted(() => {
} }
} }
} }
}
.left-center-type {
position: absolute;
top: 0;
right: 0;
height: 32px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 18px;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
color: rgb(5, 95, 194);
background-color: rgb(231, 243, 255);
align-items: center;
border-radius: 4px;
padding-left: 8px;
padding-right: 8px;
padding-top: 3px;
padding-bottom: 5px;
} }
.left-center-title { .left-center-title {
...@@ -439,7 +466,7 @@ onMounted(() => { ...@@ -439,7 +466,7 @@ onMounted(() => {
} }
.left-bottom { .left-bottom {
margin: 17px 0 0 62px; margin: 17px 0 0 59px;
ul { ul {
list-style-position: inside; list-style-position: inside;
......
<template> <template>
<div class="datasub"> <div class="datasub">
<div class="left"> <div class="left">
<div class="left-title"> <div class="left-title">
<img src="./assets/icon01.png" alt="" /> <img src="./assets/icon01.png" alt="" />
<div class="tit">各类型合作限制政策对比</div> <div class="tit">各类型合作限制政策对比</div>
<el-select v-model="value" placeholder="Select" class="select" @change="getCoopRestrictionCompareData"> <el-select v-model="value" placeholder="Select" class="select" @change="getCoopRestrictionCompareData">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div>
<div class="left-main">
<div class="left-main-echarts" ref="leftChartRef"></div>
</div> </div>
</div> <div class="left-main" :class="{ 'left-main--empty': !hasLeftChartData }">
<div class="right"> <template v-if="!hasLeftChartData">
<div class="right-title"> <el-empty class="datasub-left-empty" description="暂无数据" :image-size="100" />
<img src="./assets/icon02.png" alt="" /> </template>
<div class="tit">各领域规则分布情况</div> <template v-else>
<div class="left-chart-row">
<div ref="leftChartRef" class="left-main-echarts"></div>
</div>
<div class="source">
<TipTab :text="COOP_LEFT_TIP_TEXT" />
</div>
<div class="chart-box">
<div v-if="!isShowAiLeft" class="btn-box" @mouseenter="handleSwitchAiLeft(true)">
<AiButton />
</div>
<div v-if="isShowAiLeft" class="content-box" @mouseleave="handleSwitchAiLeft(false)">
<AiPane :aiContent="aiContentLeft" />
</div>
</div>
</template>
</div>
</div>
<div class="right">
<div class="right-title">
<img src="./assets/icon02.png" alt="" />
<div class="tit">各领域规则分布情况</div>
<el-select v-model="value1" placeholder="Select" class="select" @change="getCoopRestrictionDomainData"> <el-select v-model="value1" placeholder="Select" class="select" @change="getCoopRestrictionDomainData">
<el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in options1" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select>
</div> </div>
<div class="right-main"> <div class="right-main" :class="{ 'right-main--empty': !hasRightChartData }">
<div class="right-main-echarts" ref="rightChartRef"></div> <template v-if="!hasRightChartData">
<el-empty class="datasub-right-empty" description="暂无数据" :image-size="100" />
</template>
<template v-else>
<div class="right-chart-row">
<div ref="rightChartRef" class="right-main-echarts"></div>
</div>
<div class="source">
<TipTab :text="COOP_RIGHT_TIP_TEXT" />
</div>
<div class="chart-box">
<div v-if="!isShowAiRight" class="btn-box" @mouseenter="handleSwitchAiRight(true)">
<AiButton />
</div>
<div v-if="isShowAiRight" class="content-box" @mouseleave="handleSwitchAiRight(false)">
<AiPane :aiContent="aiContentRight" />
</div>
</div>
</template>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, onBeforeUnmount, watch } from "vue"; import { ref, onMounted, onBeforeUnmount, watch, nextTick, computed } from "vue";
import * as echarts from "echarts"; import * as echarts from "echarts";
import { getCoopRestrictionCompare, getCoopRestrictionDomain } from "@/api/coopRestriction/coopRestriction"; import { getCoopRestrictionCompare, getCoopRestrictionDomain } from "@/api/coopRestriction/coopRestriction";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import TipTab from "@/views/thinkTank/TipTab/index.vue";
import AiButton from "@/components/base/Ai/AiButton/index.vue";
import AiPane from "@/components/base/Ai/AiPane/index.vue";
const COOP_LEFT_TIP_TEXT = "各类型合作限制政策对比,数据来源:美对华科技合作限制信息平台";
const COOP_RIGHT_TIP_TEXT = "各领域规则分布情况,数据来源:美对华科技合作限制信息平台";
const value = ref(10);
const value1 = ref("2025");
const options = [
{ value: 1, label: "近一年" },
{ value: 2, label: "近两年" },
{ value: 3, label: "近三年" },
{ value: 4, label: "近四年" },
{ value: 5, label: "近五年" },
{ value: 10, label: "近十年" },
{ value: 15, label: "近十五年" },
{ value: 20, label: "近二十年" }
];
const options1 = [
{ value: "2026", label: "2026年" },
{ value: "2025", label: "2025年" },
{ value: "2024", label: "2024年" },
{ value: "2023", label: "2023年" },
{ value: "2022", label: "2022年" },
{ value: "2021", label: "2021年" },
{ value: "2020", label: "2020年" },
{ value: "2019", label: "2019年" },
{ value: "2018", label: "2018年" },
{ value: "2017", label: "2017年" },
{ value: "2016", label: "2016年" },
{ value: "2015", label: "2015年" },
{ value: "2014", label: "2014年" },
{ value: "2013", label: "2013年" },
{ value: "2012", label: "2012年" },
{ value: "2011", label: "2011年" },
{ value: "2010", label: "2010年" }
];
// 合作限制-各领域规则分布情况接口
const coopRestrictionDomain = ref([]); const coopRestrictionDomain = ref([]);
const getCoopRestrictionDomainData = async () => { const getCoopRestrictionDomainData = async () => {
try { try {
const res = await getCoopRestrictionDomain({ const res = await getCoopRestrictionDomain({
year: value1.value year: value1.value
}); });
if (res && res.code === 200) { if (res && res.code === 200) {
coopRestrictionDomain.value = res.data || []; coopRestrictionDomain.value = res.data || [];
} } else {
} catch (error) { // 接口返回 500/非 200:清空,避免继续展示上一次的图表数据
console.error("获取合作限制各领域规则分布情况数据失败:", error); coopRestrictionDomain.value = [];
} aiContentRight.value = "";
}
} catch (error) {
console.error("获取合作限制各领域规则分布情况数据失败:", error);
// 请求失败同样清空,避免展示旧图表/旧 AI
coopRestrictionDomain.value = [];
aiContentRight.value = "";
}
}; };
// 合作限制-各类型合作限制政策对比接口
const coopRestrictionCompare = ref([]); const coopRestrictionCompare = ref([]);
const getCoopRestrictionCompareData = async () => { const getCoopRestrictionCompareData = async () => {
try { try {
const res = await getCoopRestrictionCompare({ const res = await getCoopRestrictionCompare({
years: value.value years: value.value
}); });
if (res && res.code === 200) { if (res && res.code === 200) {
coopRestrictionCompare.value = res.data || []; coopRestrictionCompare.value = res.data || [];
} } else {
} catch (error) { // 接口返回 500/非 200:清空,避免继续展示上一次的图表数据
console.error("获取合作限制各类型合作限制政策对比数据失败:", error); coopRestrictionCompare.value = [];
} aiContentLeft.value = "";
}
} catch (error) {
console.error("获取合作限制各类型合作限制政策对比数据失败:", error);
// 请求失败同样清空,避免展示旧图表/旧 AI
coopRestrictionCompare.value = [];
aiContentLeft.value = "";
}
}; };
watch(() => coopRestrictionCompare.value, () => { const hasLeftChartData = computed(() => {
initLeftChart(); const list = coopRestrictionCompare.value;
}, { deep: true }); return Array.isArray(list) && list.length > 0;
});
watch(() => coopRestrictionDomain.value, () => {
initRightChart();
}, { deep: true });
const value = ref(10); const hasRightChartData = computed(() => {
const value1 = ref("2026"); const list = coopRestrictionDomain.value;
const options = [ return Array.isArray(list) && list.length > 0;
{ value: 1, label: "近一年" }, });
{ value: 2, label: "近两年" },
{ value: 3, label: "近三年" },
{ value: 4, label: "近四年" },
{ value: 5, label: "近五年" },
{ value: 10, label: "近十年" },
{ value: 15, label: "近十五年" },
{ value: 20, label: "近二十年" }
];
const options1 = [
{
value: "2026",
label: "2026年"
},
{
value: "2025",
label: "2025年"
},
{
value: "2024",
label: "2024年"
},
{
value: "2023",
label: "2023年"
},
{
value: "2022",
label: "2022年"
},
{
value: "2021",
label: "2021年"
},
{
value: "2020",
label: "2020年"
},
{
value: "2019",
label: "2019年"
},
{
value: "2018",
label: "2018年"
},
{
value: "2017",
label: "2017年"
},
{
value: "2016",
label: "2016年"
},
{
value: "2015",
label: "2015年"
},
{
value: "2014",
label: "2014年"
},
{
value: "2013",
label: "2013年"
},
{
value: "2012",
label: "2012年"
},
{
value: "2011",
label: "2011年"
},
{
value: "2010",
label: "2010年"
}
];
const leftChartRef = ref(null); const leftChartRef = ref(null);
let leftChart; let leftChart;
const rightChartRef = ref(null); const rightChartRef = ref(null);
let rightChart; let rightChart;
const isShowAiLeft = ref(true);
const aiContentLeft = ref("");
const isLeftInterpretLoading = ref(false);
const isShowAiRight = ref(true);
const aiContentRight = ref("");
const isRightInterpretLoading = ref(false);
const handleSwitchAiLeft = (val) => {
isShowAiLeft.value = val;
if (val) {
fetchLeftInterpretation();
}
};
const handleSwitchAiRight = (val) => {
isShowAiRight.value = val;
if (val) {
fetchRightInterpretation();
}
};
/** 兼容 getChartAnalysis 返回对象:从 data[0] 提取「解读」文本 */
const getInterpretationTextFromChartResponse = (res) => {
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
return (
first?.["解读"] ||
first?.["interpretation"] ||
first?.["analysis"] ||
first?.["content"] ||
""
);
};
const appendAiInterpretationChunk = (targetRef, chunk, loadingText = "解读生成中…") => {
if (!chunk) {
return;
}
const current = String(targetRef.value || "");
const base = current === loadingText ? "" : current;
targetRef.value = base + String(chunk);
};
/** 折线图解读入参(与智库概览数量变化趋势一致结构) */
const buildLeftChartPayload = () => {
const rawData = coopRestrictionCompare.value || [];
if (!rawData.length) {
return null;
}
const yearsSet = new Set();
const typesSet = new Set();
rawData.forEach((item) => {
yearsSet.add(item.LIMITDATE);
typesSet.add(item.TYPENAME);
});
const finalYears = Array.from(yearsSet).sort();
const types = Array.from(typesSet);
if (!finalYears.length || !types.length) {
return null;
}
const data = finalYears.map((year) => {
const point = { period: String(year) };
types.forEach((type) => {
const found = rawData.find((item) => item.TYPENAME === type && item.LIMITDATE === year);
point[type] = found ? found.TYPECOUNT : 0;
});
return point;
});
return {
type: "折线图",
name: "各类型合作限制政策对比",
data
};
};
const fetchLeftInterpretation = async () => {
const payload = buildLeftChartPayload();
if (!payload) {
aiContentLeft.value = "暂无图表数据";
return;
}
const hasValidContent =
aiContentLeft.value &&
aiContentLeft.value !== "解读生成中…" &&
aiContentLeft.value !== "解读加载失败" &&
aiContentLeft.value !== "暂无图表数据";
if (hasValidContent || isLeftInterpretLoading.value) {
return;
}
isLeftInterpretLoading.value = true;
aiContentLeft.value = "解读生成中…";
try {
const res = await getChartAnalysis(
{ text: JSON.stringify(payload) },
{
onChunk: (chunk) => {
// 与智库概览「数量变化趋势」一致:按 chunk 增量拼接展示
appendAiInterpretationChunk(aiContentLeft, chunk);
}
}
);
const text = getInterpretationTextFromChartResponse(res);
// 与智库概览一致:优先用最终「解读」收口;否则保留已拼接内容
aiContentLeft.value = text || aiContentLeft.value || "未返回有效解读内容";
} catch (error) {
console.error("合作限制政策对比图表解读请求失败", error);
aiContentLeft.value = "解读加载失败";
} finally {
isLeftInterpretLoading.value = false;
}
};
/** 雷达图解读入参 */
const buildRightChartPayload = () => {
const rawData = coopRestrictionDomain.value || [];
if (!rawData.length) {
return null;
}
const domainsSet = new Set();
const typesSet = new Set();
rawData.forEach((item) => {
domainsSet.add(item.AREA);
typesSet.add(item.COOPERTYPE);
});
const domains = Array.from(domainsSet);
const types = Array.from(typesSet);
if (!domains.length || !types.length) {
return null;
}
const series = types.map((type) => ({
name: type,
values: domains.map((domain) => {
const found = rawData.find((item) => item.COOPERTYPE === type && item.AREA === domain);
return {
domain,
value: found ? found.COOPERTYPECOUNT : 0
};
})
}));
return {
type: "雷达图",
name: "各领域规则分布情况",
year: value1.value,
data: series
};
};
const fetchRightInterpretation = async () => {
const payload = buildRightChartPayload();
if (!payload) {
aiContentRight.value = "";
return;
}
const hasValidContent =
aiContentRight.value &&
aiContentRight.value !== "解读生成中…" &&
aiContentRight.value !== "解读加载失败" &&
aiContentRight.value !== "暂无图表数据";
if (hasValidContent || isRightInterpretLoading.value) {
return;
}
isRightInterpretLoading.value = true;
aiContentRight.value = "解读生成中…";
try {
const res = await getChartAnalysis(
{ text: JSON.stringify(payload) },
{
onChunk: (chunk) => {
appendAiInterpretationChunk(aiContentRight, chunk);
}
}
);
const text = getInterpretationTextFromChartResponse(res);
aiContentRight.value = text || aiContentRight.value || "未返回有效解读内容";
} catch (error) {
console.error("合作限制领域分布图表解读请求失败", error);
aiContentRight.value = "解读加载失败";
} finally {
isRightInterpretLoading.value = false;
}
};
const initLeftChart = () => { const initLeftChart = () => {
if (!leftChartRef.value) return; if (!hasLeftChartData.value) {
if (leftChart) leftChart.dispose(); if (leftChart) {
leftChart.dispose();
leftChart = null;
}
return;
}
if (!leftChartRef.value) {
return;
}
if (leftChart) {
leftChart.dispose();
}
leftChart = echarts.init(leftChartRef.value); leftChart = echarts.init(leftChartRef.value);
// 处理动态数据
const rawData = coopRestrictionCompare.value; const rawData = coopRestrictionCompare.value;
const yearsSet = new Set(); const yearsSet = new Set();
const typesSet = new Set(); const typesSet = new Set();
rawData.forEach(item => { rawData.forEach((item) => {
yearsSet.add(item.LIMITDATE); yearsSet.add(item.LIMITDATE);
typesSet.add(item.TYPENAME); typesSet.add(item.TYPENAME);
}); });
const years = Array.from(yearsSet).sort(); const years = Array.from(yearsSet).sort();
const types = Array.from(typesSet); const types = Array.from(typesSet);
const finalYears = years.length > 0 ? years : [];
// 如果没有数据,给一些默认年份展示
const finalYears = years.length > 0 ? years : ["2020", "2021", "2022", "2023", "2024", "2025"]; if (!finalYears.length || !types.length) {
leftChart.dispose();
// 定义颜色配置,确保线条色与阴影色对应 leftChart = null;
return;
}
const colorMap = [ const colorMap = [
{ line: "#2f7ae5", start: "rgba(47, 122, 229, 0.3)", end: "rgba(47, 122, 229, 0.05)" }, { line: "#2f7ae5", start: "rgba(47, 122, 229, 0.3)", end: "rgba(47, 122, 229, 0.05)" },
{ line: "#29c0b1", start: "rgba(41, 192, 177, 0.3)", end: "rgba(41, 192, 177, 0.05)" }, { line: "#29c0b1", start: "rgba(41, 192, 177, 0.3)", end: "rgba(41, 192, 177, 0.05)" },
{ line: "#e45f5f", start: "rgba(228, 95, 95, 0.3)", end: "rgba(228, 95, 95, 0.05)" }, { line: "#e45f5f", start: "rgba(228, 95, 95, 0.3)", end: "rgba(228, 95, 95, 0.05)" },
{ line: "#7b5de6", start: "rgba(123, 93, 230, 0.3)", end: "rgba(123, 93, 230, 0.05)" } { line: "#7b5de6", start: "rgba(123, 93, 230, 0.3)", end: "rgba(123, 93, 230, 0.05)" }
]; ];
const series = types.map((type, index) => { const series = types.map((type, index) => {
const data = finalYears.map(year => { const data = finalYears.map((year) => {
const found = rawData.find(item => item.TYPENAME === type && item.LIMITDATE === year); const found = rawData.find((item) => item.TYPENAME === type && item.LIMITDATE === year);
return found ? found.TYPECOUNT : 0; const v = found ? found.TYPECOUNT : 0;
// 确保是整数,避免 ECharts 自动推断出小数坐标/刻度
return Math.round(Number(v) || 0);
}); });
// 根据索引获取颜色配置,超出范围则循环使用
const colorConfig = colorMap[index % colorMap.length]; const colorConfig = colorMap[index % colorMap.length];
return { return {
name: type, name: type,
type: "line", type: "line",
smooth: false, smooth: true,
data: data, data,
symbol: "circle", symbol: "circle",
symbolSize: 6, symbolSize: 6,
// 点应覆盖在折线之上,避免视觉上“穿过”节点圆
z: 3,
lineStyle: { lineStyle: {
color: colorConfig.line, color: colorConfig.line,
width: 2 width: 2
}, },
itemStyle: { itemStyle: {
color: colorConfig.line
color: "#fff",
borderColor: colorConfig.line,
borderWidth: 2
}, },
areaStyle: { areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: colorConfig.start }, { offset: 0, color: colorConfig.start },
{ offset: 1, color: colorConfig.end } { offset: 1, color: colorConfig.end }
]) ])
} },
axisLine: {
lineStyle: {
color: 'rgba(231, 241, 255, 1)'
}
},
}; };
}); });
const legendData = types.map((type, index) => ({
name: type,
itemStyle: {
// 图例需要实心圆:独立于 series 的空心点样式
color: colorMap[index % colorMap.length].line,
borderColor: colorMap[index % colorMap.length].line,
borderWidth: 0
}
}));
const maxYValue = Math.max(
0,
...series.flatMap((s) => (Array.isArray(s.data) ? s.data : []))
);
const option = { const option = {
color: colorMap.map(c => c.line), color: colorMap.map((c) => c.line),
grid: { left: 40, right: 24, top: 46, bottom: 36 }, grid: { left: 40, right: 24, top: 46, bottom: 36 },
tooltip: { trigger: "axis", axisPointer: { type: "line" } }, tooltip: { trigger: "axis", axisPointer: { type: "line" } },
legend: { legend: {
top: 8, top: 8,
icon: "circle", icon: "circle",
itemWidth: 12, itemWidth: 12,
itemHeight: 12, itemHeight: 12,
itemGap: 24, itemGap: 24,
textStyle: { textStyle: {
color: "rgb(95, 101, 108)", color: "rgb(95, 101, 108)",
fontSize: 16, fontSize: 16,
fontFamily: "Microsoft YaHei", fontFamily: "Microsoft YaHei",
fontWeight: 400, fontWeight: 400,
lineHeight: 24 lineHeight: 24
}, },
data: types data: legendData
}, },
xAxis: { xAxis: {
type: "category", type: "category",
boundaryGap: false, boundaryGap: false,
data: finalYears, data: finalYears,
axisLine: { show: false }, axisLine: {
axisTick: { show: false }, show: true,
axisLabel: { lineStyle: {
color: "rgba(132, 136, 142, 1)", color: "rgba(231, 241, 255, 1)"
fontSize: 14, }
lineHeight: 22, },
fontFamily: "Microsoft YaHei", axisTick: {
fontWeight: 400 show: true,
} // 让刻度和 label 对齐,显示短直线
alignWithLabel: true,
length: 6,
length2: 0,
lineStyle: {
color: "rgba(231, 241, 255, 1)",
type: "solid"
}
},
axisLabel: {
color: "rgba(132, 136, 142, 1)",
fontSize: 14,
lineHeight: 22,
fontFamily: "Microsoft YaHei",
fontWeight: 400
}
}, },
yAxis: { yAxis: {
type: "value", type: "value",
min: 0, min: 0,
splitLine: { show: true, lineStyle: { color: "#e6e6e6", type: "dashed" } }, // 强制整数刻度:0/1/2/3...
axisLine: { show: false }, interval: 1,
axisTick: { show: false }, max: Math.ceil(maxYValue),
axisLabel: { name: "数量",
color: "rgba(132, 136, 142, 1)", nameLocation: "end",
fontSize: 14, nameGap: 20,
fontFamily: "Microsoft YaHei", nameRotate: 0,
fontWeight: 400, nameTextStyle: {
lineHeight: 22 color: "rgba(170, 173, 177, 1)",
} fontFamily: "Microsoft YaHei",
fontWeight: 400,
fontSize: 14,
lineHeight: 22,
letterSpacing: 0,
align: "right",
verticalAlign: "bottom"
},
axisLabel: {
color: "rgba(132, 136, 142, 1)",
fontSize: 14,
fontFamily: "Microsoft YaHei",
fontWeight: 400,
lineHeight: 22,
formatter: (val) => String(Math.round(Number(val) || 0))
},
splitLine: {
show: true,
lineStyle: { color: "rgba(231, 241, 255, 1)", type: "dashed", width: 1 }
},
axisLine: { show: false },
axisTick: { show: false }
}, },
series: series series
}; };
leftChart.setOption(option); leftChart.setOption(option);
window.addEventListener("resize", handleResize);
}; };
const handleResize = () => { if (leftChart) leftChart.resize(); if (rightChart) rightChart.resize(); }; const handleResize = () => {
if (leftChart) {
leftChart.resize();
}
if (rightChart) {
rightChart.resize();
}
};
const initRightChart = () => { const initRightChart = () => {
if (!rightChartRef.value) return; if (!hasRightChartData.value) {
if (rightChart) rightChart.dispose(); if (rightChart) {
rightChart.dispose();
rightChart = null;
}
return;
}
if (!rightChartRef.value) {
return;
}
if (rightChart) {
rightChart.dispose();
}
rightChart = echarts.init(rightChartRef.value); rightChart = echarts.init(rightChartRef.value);
const rawData = coopRestrictionDomain.value; const rawData = coopRestrictionDomain.value;
// 数据为空处理
if (!rawData || rawData.length === 0) {
rightChart.setOption({
graphic: {
type: 'text',
left: 'center',
top: 'middle',
style: {
text: '暂无数据',
fill: '#999',
fontSize: 16
}
}
});
return;
}
// 1. 动态提取所有领域(雷达图的维度)
const domainsSet = new Set(); const domainsSet = new Set();
const typesSet = new Set(); const typesSet = new Set();
rawData.forEach(item => { rawData.forEach((item) => {
domainsSet.add(item.AREA); domainsSet.add(item.AREA);
typesSet.add(item.COOPERTYPE); typesSet.add(item.COOPERTYPE);
}); });
...@@ -310,18 +572,16 @@ const initRightChart = () => { ...@@ -310,18 +572,16 @@ const initRightChart = () => {
const domains = Array.from(domainsSet); const domains = Array.from(domainsSet);
const types = Array.from(typesSet); const types = Array.from(typesSet);
// 2. 构造指示器(Indicator),自动计算 Max 值 const indicators = domains.map((domain) => {
const indicators = domains.map(domain => { const domainData = rawData.filter((item) => item.AREA === domain);
const domainData = rawData.filter(item => item.AREA === domain); const maxVal = Math.max(...domainData.map((d) => d.COOPERTYPECOUNT), 5);
const maxVal = Math.max(...domainData.map(d => d.COOPERTYPECOUNT), 5); // 最小给个5 return { name: domain, max: Math.ceil(maxVal * 1.2) };
return { name: domain, max: Math.ceil(maxVal * 1.2) }; // 留 20% 的余量
}); });
// 3. 构造 Series 数据
const colorMap = ["#2f7ae5", "#29c0b1", "#e45f5f", "#7b5de6"]; const colorMap = ["#2f7ae5", "#29c0b1", "#e45f5f", "#7b5de6"];
const seriesData = types.map((type, index) => { const seriesData = types.map((type, index) => {
const dataValues = domains.map(domain => { const dataValues = domains.map((domain) => {
const found = rawData.find(item => item.COOPERTYPE === type && item.AREA === domain); const found = rawData.find((item) => item.COOPERTYPE === type && item.AREA === domain);
return found ? found.COOPERTYPECOUNT : 0; return found ? found.COOPERTYPECOUNT : 0;
}); });
...@@ -329,18 +589,19 @@ const initRightChart = () => { ...@@ -329,18 +589,19 @@ const initRightChart = () => {
name: type, name: type,
value: dataValues, value: dataValues,
itemStyle: { color: colorMap[index % colorMap.length] }, itemStyle: { color: colorMap[index % colorMap.length] },
areaStyle: { color: colorMap[index % colorMap.length], opacity: 0.1 } // 不要填充多边形:让雷达图“圆里面是空的”
// areaStyle 不设置(或设为 0)可避免穿透同心圆的填充效果
}; };
}); });
const option = { const option = {
color: colorMap, color: colorMap,
legend: { legend: {
top: 8, top: 8,
icon: "circle", icon: "circle",
itemWidth: 12, itemWidth: 12,
itemHeight: 12, itemHeight: 12,
itemGap: 24, itemGap: 24,
textStyle: { textStyle: {
color: "rgb(95, 101, 108)", color: "rgb(95, 101, 108)",
fontSize: 16, fontSize: 16,
...@@ -353,11 +614,7 @@ const initRightChart = () => { ...@@ -353,11 +614,7 @@ const initRightChart = () => {
radar: { radar: {
center: ["50%", "55%"], center: ["50%", "55%"],
radius: "65%", radius: "65%",
indicator: indicators.length > 0 ? indicators : [ indicator: indicators,
{ name: "暂无数据", max: 100 },
{ name: "暂无数据", max: 100 },
{ name: "暂无数据", max: 100 }
],
axisName: { axisName: {
color: "rgba(132, 136, 142, 1)", color: "rgba(132, 136, 142, 1)",
fontSize: 14, fontSize: 14,
...@@ -366,66 +623,110 @@ const initRightChart = () => { ...@@ -366,66 +623,110 @@ const initRightChart = () => {
splitLine: { lineStyle: { color: ["#e6e6e6"] } }, splitLine: { lineStyle: { color: ["#e6e6e6"] } },
splitArea: { show: false } splitArea: { show: false }
}, },
series: [{ series: [
type: "radar", {
data: seriesData type: "radar",
}] data: seriesData,
// 移除雷达图端点圆点(既不显示实心圆,也不显示空心圆)
symbol: "none",
symbolSize: 0,
showSymbol: false
}
]
}; };
rightChart.setOption(option); rightChart.setOption(option);
window.addEventListener("resize", handleResize);
}; };
onMounted(() => { watch(
// 合作限制-各类型合作限制政策对比接口 coopRestrictionCompare,
getCoopRestrictionCompareData(); async () => {
// 合作限制-各领域规则分布情况接口 aiContentLeft.value = "";
getCoopRestrictionDomainData(); await nextTick();
initLeftChart(); initLeftChart();
initRightChart(); if (isShowAiLeft.value && hasLeftChartData.value) {
fetchLeftInterpretation();
}
},
{ deep: true, immediate: true }
);
watch(
coopRestrictionDomain,
async () => {
aiContentRight.value = "";
await nextTick();
initRightChart();
if (isShowAiRight.value && hasRightChartData.value) {
fetchRightInterpretation();
}
},
{ deep: true, immediate: true }
);
onMounted(async () => {
window.addEventListener("resize", handleResize);
await getCoopRestrictionCompareData();
await getCoopRestrictionDomainData();
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
if (leftChart) {
leftChart.dispose();
leftChart = null;
}
if (rightChart) {
rightChart.dispose();
rightChart = null;
}
}); });
onBeforeUnmount(() => { window.removeEventListener("resize", handleResize); if (leftChart) { leftChart.dispose(); leftChart = null; } if (rightChart) { rightChart.dispose(); rightChart = null; } });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.datasub { .datasub {
width: 1600px; width: 1600px;
height: 460px; height: 460px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
.left {
width: 1063px; .left {
height: 460px; width: 1063px;
margin-right: 16px; height: 460px;
border-radius: 10px; margin-right: 16px;
// border: 1px solid rgb(234, 236, 238); border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background-color: #fff; background-color: #fff;
.left-title {
width: 1063px; .left-title {
height: 48px; width: 1063px;
border-bottom: 1px solid rgb(234, 236, 238); height: 48px;
position: relative; border-bottom: 1px solid rgb(234, 236, 238);
img { position: relative;
width: 19px;
height: 19px; img {
position: absolute; width: 19px;
top: 16px; height: 19px;
left: 21px; position: absolute;
} top: 16px;
.tit { left: 21px;
margin-left: 60px; }
height: 48px;
padding: 11px 0; .tit {
font-size: 20px; margin-left: 60px;
font-weight: 700; height: 48px;
font-family: "Microsoft YaHei"; padding: 11px 0;
line-height: 26px; font-size: 20px;
color: rgb(5, 95, 194); font-weight: 700;
} font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.select { .select {
width: 150px; width: 150px;
height: 28px; height: 28px;
...@@ -434,46 +735,104 @@ onBeforeUnmount(() => { window.removeEventListener("resize", handleResize); if ( ...@@ -434,46 +735,104 @@ onBeforeUnmount(() => { window.removeEventListener("resize", handleResize); if (
top: 11px; top: 11px;
right: 31px; right: 31px;
} }
} }
.left-main { .left-main {
width: 1063px; width: 1063px;
height: 412px; height: 412px;
padding: 15px 23px 25px 38px; box-sizing: border-box;
position: relative;
padding: 24px 24px 65px 24px;
&.left-main--empty {
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
:deep(.el-empty__image) {
margin-bottom: 0;
}
}
.datasub-left-empty {
padding: 0;
margin: 0;
}
.left-chart-row {
display: flex;
flex-direction: row;
align-items: flex-start;
width: 100%;
box-sizing: border-box;
}
.left-main-echarts { .left-main-echarts {
width: 1002px; width: 1015px;
height: 372px; height: 323px;
}
.source {
position: absolute;
bottom: 21px;
left: 24px;
height: 22px;
display: flex;
}
.chart-box {
position: absolute;
right: 0;
bottom: 18px;
.btn-box {
width: 74px;
height: 28px;
}
.content-box {
width: 1063px;
position: absolute;
right: 0;
bottom: -18px;
}
} }
} }
} }
.right {
width: 521px; .right {
height: 460px; width: 521px;
border-radius: 10px; height: 460px;
// border: 1px solid rgb(234, 236, 238); border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background-color: #fff; background-color: #fff;
.right-title {
width: 521px; .right-title {
height: 48px; width: 521px;
border-bottom: 1px solid rgb(234, 236, 238); height: 48px;
position: relative; border-bottom: 1px solid rgb(234, 236, 238);
img { position: relative;
width: 19px;
height: 19px; img {
position: absolute; width: 19px;
top: 16px; height: 19px;
left: 21px; position: absolute;
} top: 16px;
.tit { left: 21px;
margin-left: 60px; }
height: 48px;
padding: 11px 0; .tit {
font-size: 20px; margin-left: 60px;
font-weight: 700; height: 48px;
font-family: "Microsoft YaHei"; padding: 11px 0;
line-height: 26px; font-size: 20px;
color: rgb(5, 95, 194); font-weight: 700;
} font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.select { .select {
width: 150px; width: 150px;
height: 28px; height: 28px;
...@@ -482,16 +841,68 @@ onBeforeUnmount(() => { window.removeEventListener("resize", handleResize); if ( ...@@ -482,16 +841,68 @@ onBeforeUnmount(() => { window.removeEventListener("resize", handleResize); if (
top: 11px; top: 11px;
right: 31px; right: 31px;
} }
} }
.right-main { .right-main {
width: 521px; width: 521px;
height: 421px; height: 412px;
padding: 15px 50px 25px 50px; box-sizing: border-box;
padding: 24px 24px 64px 24px;
position: relative;
&.right-main--empty {
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
:deep(.el-empty__image) {
margin-bottom: 0;
}
}
.datasub-right-empty {
padding: 0;
margin: 0;
}
.right-chart-row {
display: flex;
width: 100%;
box-sizing: border-box;
}
.right-main-echarts { .right-main-echarts {
width: 420px; width: 473px;
height: 372px; height: 324px;
}
.source {
position: absolute;
bottom: 21px;
left: 24px;
height: 22px;
display: flex;
}
.chart-box {
position: absolute;
right: 0;
bottom: 18px;
.btn-box {
width: 74px;
height: 28px;
}
.content-box {
width: 520px;
position: absolute;
right: 0;
bottom: -18px;
}
} }
} }
} }
} }
</style> </style>
<template> <template>
<div class="reslib-page" ref="reslibContainer"> <div class="reslib-page" ref="reslibContainer">
<div class="nav"> <div class="nav">
<div <div v-for="item in navList" :key="item.id" class="nav-item" :class="{ active: item.id === activeItem }"
v-for="item in navList" @click="activeItem = item.id">
:key="item.id"
class="nav-item"
:class="{ active: item.id === activeItem }"
@click="activeItem = item.id"
>
{{ item.name }} {{ item.name }}
</div> </div>
</div> </div>
<el-select v-model="value" placeholder="排序方式" class="select"> <el-select v-model="sortModel" placeholder="发布时间" class="select" :teleported="true" placement="bottom-start"
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" /> :popper-options="sortPopperOptions" @change="handleSortChange">
<template #prefix>
<img v-if="sortModel !== true" src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image down.png"
class="select-prefix-img" alt="" @click.stop="toggleSortPrefix" />
<img v-else src="@/views/thinkTank/ThinkTankDetail/thinkDynamics/images/image up.png" class="select-prefix-img"
alt="" @click.stop="toggleSortPrefix" />
</template>
<el-option :key="true" label="正序" :value="true" />
<el-option :key="false" label="倒序" :value="false" />
</el-select> </el-select>
<div class="main"> <div class="main">
<div class="left"> <div class="left">
<div class="left-ti1"></div> <div class="left-ti1"></div>
<div class="left-ti2"></div>
<div class="left-title">数据来源</div>
<div class="left-title ">科技领域</div>
<div class="left-content"> <div class="left-content">
<div v-for="item in dataList" :key="item.id" class="left-item"> <div v-for="item in dataList2" :key="item.id" class="left-item">
<input <input type="checkbox" :value="String(item.id)" v-model="selectedDomains" />{{ item.name }}
type="checkbox"
:value="String(item.id)"
v-model="selectedSources"
/>{{ item.name }}
</div> </div>
</div> </div>
<div class="left-title cl1">涉及领域</div> <div class="left-ti2"></div>
<div class="left-title">数据来源</div>
<div class="left-content"> <div class="left-content">
<div v-for="item in dataList2" :key="item.id" class="left-item"> <div v-for="item in dataList" :key="item.id" class="left-item">
<input <input type="checkbox" :value="String(item.id)" v-model="selectedSources" />{{ item.name }}
type="checkbox"
:value="String(item.id)"
v-model="selectedDomains"
/>{{ item.name }}
</div> </div>
</div> </div>
</div> </div>
...@@ -47,30 +44,26 @@ ...@@ -47,30 +44,26 @@
<div class="right-main"> <div class="right-main">
<div class="main-content"> <div class="main-content">
<div v-for="item in mainDataList" :key="item.id" class="main-item"> <div v-for="item in mainDataList" :key="item.id" class="main-item">
<div class="date">{{ item.date }}</div> <div class="date">{{ formatDateCn(item.date) }}</div>
<img :src="item.img" alt="" class="img" /> <img :src="item.img" alt="" class="img" />
<div class="box"> <div class="box">
<div class="title" @click="handleClick(item)">{{ item.title }}</div> <div class="title" @click="handleClick(item)">{{ item.title }}</div>
<div class="content" @click="handleClick(item)">{{ item.content }}</div> <div class="content" @click="handleClick(item)">{{ item.content }}</div>
<div class="domain"> <div class="domain">
<div v-for="(domain, i) in item.domain" :key="i" class="domain-item">{{ domain }}</div> <AreaTag v-for="(domain, i) in item.domain" :key="i" " :tagName="domain">
</AreaTag>
</div> </div>
<div class="type" :class="getTypeClass(item.type)"> <div class="type" :class="getTypeClass(item.type)">
{{ item.type }} {{ item.type }}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="page"> <div class="page">
<div class="count">{{ total }}</div> <div class="count">共 {{ total }} 项调查</div>
<el-pagination <el-pagination v-model:current-page="currentPage" :page-size="pageSize" :total="total"
v-model:current-page="currentPage" layout="prev, pager, next" background @current-change="handlePageChange" />
:page-size="pageSize"
:total="total"
layout="prev, pager, next"
background
@current-change="handlePageChange"
/>
</div> </div>
</div> </div>
</div> </div>
...@@ -79,12 +72,23 @@ ...@@ -79,12 +72,23 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from "vue"; import { ref, onMounted, watch, computed } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { getCoopRestrictionList } from "@/api/coopRestriction/coopRestriction"; import { getCoopRestrictionList } from "@/api/coopRestriction/coopRestriction";
import defaultImg from "../../assets/images/default-icon2.png"; import defaultImg from "../../assets/images/default-icon2.png";
const formatDateCn = (dateStr) => {
const s = String(dateStr || "").trim();
// 兼容 YYYY-MM-DD / YYYY/MM/DD
const m = s.match(/^(\d{4})[-/](\d{1,2})[-/](\d{1,2})/);
if (!m) return s;
const y = m[1];
const month = String(Number(m[2]) || "");
const day = String(Number(m[3]) || "");
return `${y}\n${month}${day}日`;
};
// 合作限制-获取合作限制列表接口 // 合作限制-获取合作限制列表接口
const getMainDataList = async () => { const getMainDataList = async () => {
const params = { const params = {
...@@ -102,12 +106,11 @@ const getMainDataList = async () => { ...@@ -102,12 +106,11 @@ const getMainDataList = async () => {
if (activeItem.value !== "0") { if (activeItem.value !== "0") {
params.type = activeItem.value; params.type = activeItem.value;
} }
if (value.value) { // 与智库概览一致:null 作为占位但默认按倒序;true=正序,false=倒序
params.sortOrder = value.value; params.sortOrder = sort.value === true ? "asc" : "desc";
}
try { try {
console.log('----params getMainDataList', params) console.log('----params getMainDataList', params)
const res = await getCoopRestrictionList(params); const res = await getCoopRestrictionList(params);
if (res && res.code === 200) { if (res && res.code === 200) {
mainDataList.value = (res.data.content || []).map(item => ({ mainDataList.value = (res.data.content || []).map(item => ({
...@@ -153,18 +156,43 @@ const navList = ref([ ...@@ -153,18 +156,43 @@ const navList = ref([
]); ]);
const activeItem = ref("0"); const activeItem = ref("0");
const value = ref(""); /** null:占位「发布时间」且默认倒序;true 正序;false 倒序(显式),与智库概览一致 */
const sort = ref(null);
const options = [ const sortPopperOptions = {
{ modifiers: [
value: "asc", { name: "preventOverflow", options: { mainAxis: false, altAxis: false } },
label: "正序" { name: "flip", enabled: false }
]
};
const sortModel = computed({
get() {
return sort.value;
}, },
{ set(v) {
value: "desc", sort.value = v;
label: "倒序" }
});
const handleSortChange = () => {
// 改变排序后从第一页开始
if (currentPage.value === 1) {
getMainDataList();
} else {
currentPage.value = 1;
}
};
const toggleSortPrefix = () => {
sort.value = sort.value === true ? false : true;
// 切换排序后从第一页开始
if (currentPage.value === 1) {
getMainDataList();
} else {
currentPage.value = 1;
} }
]; };
const dataList = ref([ const dataList = ref([
{ {
id: "0", id: "0",
...@@ -276,7 +304,7 @@ onMounted(() => { ...@@ -276,7 +304,7 @@ onMounted(() => {
}); });
watch( watch(
[activeItem, selectedSources, selectedDomains, value], [activeItem, selectedSources, selectedDomains, sort],
(newVal, oldVal) => { (newVal, oldVal) => {
const [newActive, newSources, newDomains] = newVal; const [newActive, newSources, newDomains] = newVal;
const [, oldSources, oldDomains] = oldVal; const [, oldSources, oldDomains] = oldVal;
...@@ -284,7 +312,7 @@ watch( ...@@ -284,7 +312,7 @@ watch(
if (newSources.includes("0") && newSources.length > 1) { if (newSources.includes("0") && newSources.length > 1) {
if (oldSources.includes("0")) { if (oldSources.includes("0")) {
selectedSources.value = newSources.filter(i => i !== "0"); selectedSources.value = newSources.filter(i => i !== "0");
return; return;
} else { } else {
selectedSources.value = ["0"]; selectedSources.value = ["0"];
return; return;
...@@ -326,12 +354,13 @@ watch(currentPage, () => { ...@@ -326,12 +354,13 @@ watch(currentPage, () => {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.reslib-page { .reslib-page {
width: 1600px; width: 1600px;
min-height: 1565px;
height: auto;
position: relative; position: relative;
padding-bottom: 50px; padding-bottom: 50px;
.nav { .nav {
width: 808px; width: 808px;
height: 42px; height: 42px;
...@@ -339,6 +368,7 @@ watch(currentPage, () => { ...@@ -339,6 +368,7 @@ watch(currentPage, () => {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
margin-bottom: 34px; margin-bottom: 34px;
.nav-item { .nav-item {
cursor: pointer; cursor: pointer;
padding: 8px 20px; padding: 8px 20px;
...@@ -348,6 +378,7 @@ watch(currentPage, () => { ...@@ -348,6 +378,7 @@ watch(currentPage, () => {
line-height: 26px; line-height: 26px;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
} }
.active { .active {
background-color: rgb(5, 95, 194); background-color: rgb(5, 95, 194);
border-radius: 21px; border-radius: 21px;
...@@ -355,25 +386,44 @@ watch(currentPage, () => { ...@@ -355,25 +386,44 @@ watch(currentPage, () => {
font-weight: 700; font-weight: 700;
} }
} }
.select { .select {
width: 128px; width: 128px;
position: absolute; position: absolute;
top: 7px; top: 7px;
right: 0px; right: 0px;
} }
.select-prefix-img {
width: 7px;
height: 14px;
margin-right: 6px;
cursor: pointer;
}
/* 下拉项内边距:与智库模块 el-select 视觉一致 */
:deep(.el-select-dropdown__item) {
padding: 0 20px;
}
.main { .main {
width: 1600px; width: 1600px;
height: auto;
min-height: 1489px;
display: flex; display: flex;
margin-bottom: 35px;
.left { .left {
width: 300px; width: 360px;
height: 760px; height: 432px;
padding-bottom: 24px;
box-sizing: border-box;
border: 1px solid rgba(234, 236, 238, 1);
margin-right: 16px; margin-right: 16px;
border-radius: 10px; border-radius: 10px;
background-color: #fff; background-color: #fff;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(94, 95, 95, 0.1);
position: relative; position: relative;
.left-ti1 { .left-ti1 {
width: 8px; width: 8px;
height: 16px; height: 16px;
...@@ -381,9 +431,10 @@ watch(currentPage, () => { ...@@ -381,9 +431,10 @@ watch(currentPage, () => {
border-top-right-radius: 3px; border-top-right-radius: 3px;
border-bottom-right-radius: 3px; border-bottom-right-radius: 3px;
position: absolute; position: absolute;
top: 17px; top: 20px;
left: 0px; left: 0px;
} }
.left-ti2 { .left-ti2 {
width: 8px; width: 8px;
height: 16px; height: 16px;
...@@ -391,47 +442,62 @@ watch(currentPage, () => { ...@@ -391,47 +442,62 @@ watch(currentPage, () => {
border-top-right-radius: 3px; border-top-right-radius: 3px;
border-bottom-right-radius: 3px; border-bottom-right-radius: 3px;
position: absolute; position: absolute;
top: 207px; top: 320px;
left: 0px; left: 0px;
} }
.left-title { .left-title {
margin-left: 25px; margin-left: 25px;
color: rgb(5, 95, 194); margin-top: 16px;
color: var(--color-main-active);
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei"; font-family: "Source Han Sans CN";
line-height: 24px; line-height: 24px;
margin-top: 13px; letter-spacing: 1px;
height: 24px;
} }
.left-content { .left-content {
// width: 109px; margin-left: 24px;
// height: 132px; margin-top: 12px;
margin-left: 25px; display: grid;
margin-top: 13px; grid-template-columns: repeat(2, 160px);
gap: 8px 4px;
.left-item { .left-item {
// width: 89px; width: 160px;
height: 30px; height: 24px;
margin-bottom: 4px; margin: 0;
display: flex;
align-items: center;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Source Han Sans CN";
line-height: 24px; line-height: 24px;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
input[type="checkbox"] { input[type="checkbox"] {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
width: 14px; width: 14px;
height: 14px; height: 14px;
margin-right: 8px; margin-right: 8px;
flex: 0 0 auto;
border: 1px solid rgb(200, 204, 210); border: 1px solid rgb(200, 204, 210);
border-radius: 4px; border-radius: 4px;
background-color: #fff; background-color: #fff;
vertical-align: middle; vertical-align: middle;
} }
input[type="checkbox"]:checked { input[type="checkbox"]:checked {
background-color: rgb(5, 95, 194); background-color: rgb(5, 95, 194);
border-color: rgb(5, 95, 194); border-color: rgb(5, 95, 194);
} }
input[type="checkbox"]:checked::after { input[type="checkbox"]:checked::after {
content: ""; content: "";
display: block; display: block;
...@@ -445,22 +511,23 @@ watch(currentPage, () => { ...@@ -445,22 +511,23 @@ watch(currentPage, () => {
} }
} }
} }
.cl1 {
margin-top: 21px;
}
} }
.right { .right {
width: 1284px; width: 1224px;
height: auto;
min-height: 1489px;
border-radius: 10px; border-radius: 10px;
background-color: #fff; background-color: #fff;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
.right-title { .right-title {
width: 1284px; width: 1224px;
height: 48px; height: 48px;
border-bottom: 1px solid rgb(235, 238, 242); border-bottom: 1px solid rgb(235, 238, 242);
position: relative; position: relative;
img { img {
width: 22px; width: 22px;
height: 19px; height: 19px;
...@@ -468,6 +535,7 @@ watch(currentPage, () => { ...@@ -468,6 +535,7 @@ watch(currentPage, () => {
top: 15px; top: 15px;
left: 20px; left: 20px;
} }
div { div {
width: 120px; width: 120px;
height: 48px; height: 48px;
...@@ -480,69 +548,89 @@ watch(currentPage, () => { ...@@ -480,69 +548,89 @@ watch(currentPage, () => {
padding: 11px 0; padding: 11px 0;
} }
} }
.right-main { .right-main {
width: 1284px; width: 1224px;
height: auto;
min-height: 1441px; padding: 12px 0px 80px 0px;
padding: 22px 43px 80px 20px;
position: relative; position: relative;
.main-content { .main-content {
width: 1221px; width: 1224px;
height: auto; padding-left: 28px;
min-height: 1345px; border-bottom: 1px solid rgb(235, 238, 242);
.main-item { .main-item {
width: 1221px; width: 1167px;
height: auto;
min-height: 116px;
margin-bottom: 24px;
position: relative; position: relative;
display: flex; display: flex;
&::after { &::after {
content: ""; content: "";
position: absolute; position: absolute;
top: 37px; top: 37px;
bottom: -37px; bottom: -37px;
left: 91px; left: 108px;
width: 2px; width: 2px;
background-color: rgb(230, 231, 232); background-color: rgb(230, 231, 232);
z-index: 1; z-index: 1;
} }
&:last-child::after { &:last-child::after {
display: none; content: "";
position: absolute;
top: 37px;
bottom: -37px;
left: 108px;
width: 2px;
background-color: rgb(230, 231, 232);
z-index: 1;
height: calc(100% - 37px);
} }
.date { .date {
flex-shrink: 0; flex-shrink: 0;
width: 62px; width: 80px;
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 24px; line-height: 24px;
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
text-align: right; text-align: right;
margin-top: 13px; margin-top: 6px;
white-space: pre-line;
} }
.img { .img {
flex-shrink: 0; flex-shrink: 0;
width: 24px; width: 24px;
height: 24px; height: 24px;
margin: 13px 21px 0 18px; margin: 14px 16px 0 16px;
position: relative; position: relative;
z-index: 100; z-index: 100;
} }
.box { .box {
flex: 1; flex: 1;
min-height: 91px; min-height: 91px;
position: relative; position: relative;
padding-top: 10px; padding-top: 14px;
.title { .title {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
line-height: 26px; line-height: 26px;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
margin-bottom: 8px; margin-bottom: 9px;
cursor: pointer; cursor: pointer;
} }
.content { .content {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
...@@ -552,6 +640,7 @@ watch(currentPage, () => { ...@@ -552,6 +640,7 @@ watch(currentPage, () => {
margin-bottom: 9px; margin-bottom: 9px;
cursor: pointer; cursor: pointer;
} }
.type { .type {
padding: 2px 8px; padding: 2px 8px;
border-radius: 20px; border-radius: 20px;
...@@ -563,29 +652,37 @@ watch(currentPage, () => { ...@@ -563,29 +652,37 @@ watch(currentPage, () => {
top: 10px; top: 10px;
right: 0px; right: 0px;
} }
.type1 { .type1 {
background-color: rgba(255, 149, 77, 0.1); background-color: rgba(255, 149, 77, 0.1);
color: rgb(255, 149, 77); color: rgb(255, 149, 77);
} }
.type2 { .type2 {
background-color: rgba(206, 79, 81, 0.1); background-color: rgba(206, 79, 81, 0.1);
color: rgb(206, 79, 81); color: rgb(206, 79, 81);
} }
.type3 { .type3 {
background-color: rgba(5, 95, 194, 0.1); background-color: rgba(5, 95, 194, 0.1);
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
} }
.type4 { .type4 {
background-color: rgba(103, 194, 58, 0.1); background-color: rgba(103, 194, 58, 0.1);
color: rgb(103, 194, 58); color: rgb(103, 194, 58);
} }
.type-default { .type-default {
background-color: rgba(144, 147, 153, 0.1); background-color: rgba(144, 147, 153, 0.1);
color: rgb(144, 147, 153); color: rgb(144, 147, 153);
} }
.domain { .domain {
margin-bottom: 15px; margin-bottom: 12px;
display: flex; display: flex;
gap: 8px;
.domain-item { .domain-item {
padding: 2px 8px; padding: 2px 8px;
border-radius: 4px; border-radius: 4px;
...@@ -601,8 +698,9 @@ watch(currentPage, () => { ...@@ -601,8 +698,9 @@ watch(currentPage, () => {
} }
} }
} }
.page { .page {
width: 1221px; width: 1159px;
height: 40px; height: 40px;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -611,6 +709,7 @@ watch(currentPage, () => { ...@@ -611,6 +709,7 @@ watch(currentPage, () => {
bottom: 20px; bottom: 20px;
left: 20px; left: 20px;
padding-left: 11px; padding-left: 11px;
.count { .count {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
...@@ -618,10 +717,12 @@ watch(currentPage, () => { ...@@ -618,10 +717,12 @@ watch(currentPage, () => {
line-height: 24px; line-height: 24px;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
} }
:deep(.el-pagination) { :deep(.el-pagination) {
display: flex; display: flex;
align-items: center; align-items: center;
} }
:deep(.el-pagination.is-background .el-pager li) { :deep(.el-pagination.is-background .el-pager li) {
min-width: 32px; min-width: 32px;
height: 32px; height: 32px;
...@@ -635,12 +736,14 @@ watch(currentPage, () => { ...@@ -635,12 +736,14 @@ watch(currentPage, () => {
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
} }
:deep(.el-pagination.is-background .el-pager li.is-active) { :deep(.el-pagination.is-background .el-pager li.is-active) {
background-color: #fff; background-color: #fff;
color: rgba(22, 119, 255, 1); color: rgba(22, 119, 255, 1);
border-color: rgba(22, 119, 255, 1); border-color: rgba(22, 119, 255, 1);
// box-shadow: 0 0 0 2px rgba(47, 122, 229, 0.15) inset; // box-shadow: 0 0 0 2px rgba(47, 122, 229, 0.15) inset;
} }
:deep(.el-pagination.is-background .el-pager li.is-ellipsis) { :deep(.el-pagination.is-background .el-pager li.is-ellipsis) {
border: none; border: none;
background-color: transparent; background-color: transparent;
...@@ -648,6 +751,7 @@ watch(currentPage, () => { ...@@ -648,6 +751,7 @@ watch(currentPage, () => {
min-width: 16px; min-width: 16px;
margin: 0 6px; margin: 0 6px;
} }
:deep(.el-pagination.is-background .btn-prev), :deep(.el-pagination.is-background .btn-prev),
:deep(.el-pagination.is-background .btn-next) { :deep(.el-pagination.is-background .btn-next) {
min-width: 32px; min-width: 32px;
...@@ -660,6 +764,7 @@ watch(currentPage, () => { ...@@ -660,6 +764,7 @@ watch(currentPage, () => {
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
margin: 0 6px; margin: 0 6px;
} }
:deep(.el-pagination.is-background .btn-prev.is-disabled), :deep(.el-pagination.is-background .btn-prev.is-disabled),
:deep(.el-pagination.is-background .btn-next.is-disabled) { :deep(.el-pagination.is-background .btn-next.is-disabled) {
color: rgba(95, 101, 108, 0.45); color: rgba(95, 101, 108, 0.45);
......
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
<div class="nav-main"> <div class="nav-main">
<img :src="coopData?.IMAGEURL || defaultImg" alt="" /> <img :src="coopData?.IMAGEURL || defaultImg" alt="" />
<div class="content"> <div class="content">
<div class="cl1">{{coopData?.LIMITNAMEZH}}</div> <div class="cl1">{{ coopData?.LIMITNAMEZH }}</div>
<div class="cl2">{{coopData?.LIMITNAME}}</div> <div class="cl2">{{ coopData?.LIMITNAME }}</div>
<div class="cl3">{{coopData?.LIMITDATE}} · {{ coopData?.LIMITORGNAME }}</div> <div class="cl3">{{ coopData?.LIMITDATE }} · {{ coopData?.LIMITORGNAME }}</div>
</div> </div>
<div class="btn"> <div class="btn">
<button class="btn1"><img src="./assets/icon01.png" alt="" />查看原文</button> <button class="btn1"><img src="./assets/icon01.png" alt="" />查看原文</button>
...@@ -15,45 +15,42 @@ ...@@ -15,45 +15,42 @@
</div> </div>
</div> </div>
</div> </div>
<div class="title" v-if="coopData?.Relation?.[0]" @click="handleClick"> <div class="title" v-if="coopData.Relation?.[0]" @click="handleClick">
<span class="title-one">当前合作限制数据已关联至{{coopData.Relation[0]?.TYPE}}</span> <span class="title-one">当前合作限制数据已关联至{{ coopData.Relation[0]?.TYPE }}</span>
<span class="title-two">{{coopData.Relation[0]?.RELATIONNAME}}{{coopData.Relation[0]?.RELATIONDATE}}</span> <span class="title-two">{{ coopData.Relation[0]?.RELATIONNAME }}{{ coopData.Relation[0]?.RELATIONDATE }}</span>
<img src="./assets/right.png" alt="" /> <img src="./assets/right.png" alt="" />
</div> </div>
<div class="main"> <div class="main">
<div class="left"> <div class="left">
<!-- 制裁概况 --> <!-- 制裁概况 -->
<div class="left-top"> <div class="left-top">
<img class="img1" src="./assets/bluetitle.png" alt="" /> <AnalysisBox title="制裁概况" :showAllBtn="true">
<div class="left-top-title">制裁概况</div> <div class="box1-main">
<img class="img2" src="./assets/下载按钮.png" alt="" /> <div class="left-top-content">
<img class="img3" src="./assets/收藏按钮.png" alt="" /> <span>{{ coopData?.INTRODUCTION }}</span>
<div class="left-top-content"> </div>
<span <div class="left-top-bottom">
>{{ coopData?.INTRODUCTION }}</span <div><span class="tit1">限制时间:</span><span class="tit2">{{ coopData?.LIMITDATE }}</span></div>
> <div @click="handleClickOnLimitOrg" style="cursor: pointer;"><span class="tit1">限制机构:</span><span
</div> class=" tit3">{{ coopData?.LIMITORGNAME }} ></span></div>
<div class="left-top-bottom"> <div><span class="tit1">限制手段:</span><span class="tit2">{{ coopData?.LIMITMEANS }}</span></div>
<div><span class="tit">限制时间:</span><span class="tit1">{{ coopData?.LIMITDATE }}</span></div> <div><span class="tit1">限制类型:</span><span class="tit4">{{ coopData?.LIMITTYPE }}</span></div>
<div @click="handleClickOnLimitOrg" style="cursor: pointer;"><span class="tit">限制机构:</span><span class="tit1 tit2">{{ coopData?.LIMITORGNAME }} ></span></div> <div><span class="tit1">限制领域:</span><span class="tit2">{{ coopData?.area }}</span></div>
<div><span class="tit">限制手段:</span><span class="tit1">{{ coopData?.LIMITMEANS }}</span></div> </div>
<div><span class="tit">限制类型:</span><span class="tit1 tit3">{{ coopData?.LIMITTYPE }}</span></div> </div>
<div><span class="tit">限制领域:</span><span class="tit1">{{ coopData?.area }}</span></div> </AnalysisBox>
</div>
</div> </div>
<!-- 相关实体 --> <!-- 相关实体 -->
<div class="left-bottom"> <div class="left-bottom">
<img class="img1" src="./assets/bluetitle.png" alt="" /> <AnalysisBox title="相关实体" :showAllBtn="true">
<div class="left-bottom-title">相关实体</div> <div class="left-bottom-main">
<img class="img2" src="./assets/下载按钮.png" alt="" /> <div v-for="item in coopRelatedData" :key="item.id" class="main-box" @click="handleClickOnEntity(item)">
<img class="img3" src="./assets/收藏按钮.png" alt="" /> <img :src="item.img || defaultCom" alt="" />
<div class="left-bottom-main"> <div class="name">{{ item.ENTITYNAME }}</div>
<div v-for="item in coopRelatedData" :key="item.id" class="main-box" @click="handleClickOnEntity(item)"> <div class="type">{{ item.type }}</div>
<img :src="item.img || defaultCom" alt="" /> </div>
<div class="name">{{ item.ENTITYNAME }}</div>
<div class="type">{{ item.type }}</div>
</div> </div>
</div> </AnalysisBox>
</div> </div>
</div> </div>
<div class="right"> <div class="right">
...@@ -75,8 +72,8 @@ ...@@ -75,8 +72,8 @@
<div class="right-bottom"> <div class="right-bottom">
<img class="img1" src="./assets/bluetitle.png" alt="" /> <img class="img1" src="./assets/bluetitle.png" alt="" />
<div class="right-bottom-title">限制条款</div> <div class="right-bottom-title">限制条款</div>
<div class="btn cl1" :class="{'active': active2 === '涉华背景'}" @click="active2 = '涉华背景'">涉华背景</div> <div class="btn cl1" :class="{ 'active': active2 === '涉华条款' }" @click="active2 = '涉华条款'">涉华条款</div>
<div class="btn cl2" :class="{'active': active2 === '全部背景'}" @click="active2 = '全部背景'">全部背景</div> <div class="btn cl2" :class="{ 'active': active2 === '全部条款' }" @click="active2 = '全部条款'">全部条款</div>
<div class="right-bottom-content"> <div class="right-bottom-content">
<div v-for="(item, index) in filteredClauseList" :key="index" class="clause-item"> <div v-for="(item, index) in filteredClauseList" :key="index" class="clause-item">
<div class="clause-item-title"> <div class="clause-item-title">
...@@ -122,14 +119,16 @@ const getlimitClauseData = async () => { ...@@ -122,14 +119,16 @@ const getlimitClauseData = async () => {
limitId: route.query.id limitId: route.query.id
}); });
if (res && res.code === 200) { if (res && res.code === 200) {
limitClauseData.value = res.data || []; // 兼容后端返回:数组 / 单对象 / 包一层 data
const raw = res?.data?.data ?? res?.data ?? [];
limitClauseData.value = Array.isArray(raw) ? raw : raw ? [raw] : [];
} else { } else {
limitClauseData.value = []; limitClauseData.value = [];
} }
} catch (error) { } catch (error) {
console.error("获取合作限制限制条款数据失败:", error); console.error("获取合作限制限制条款数据失败:", error);
limitClauseData.value = []; limitClauseData.value = [];
} }
}; };
...@@ -150,7 +149,7 @@ const getbackgroundData = async () => { ...@@ -150,7 +149,7 @@ const getbackgroundData = async () => {
} catch (error) { } catch (error) {
console.error("获取合作限制背景分析数据失败:", error); console.error("获取合作限制背景分析数据失败:", error);
backgroundList.value = []; backgroundList.value = [];
} }
}; };
...@@ -171,7 +170,7 @@ const getcoopRelatedData = async () => { ...@@ -171,7 +170,7 @@ const getcoopRelatedData = async () => {
} catch (error) { } catch (error) {
console.error("获取合作限制相关实体数据失败:", error); console.error("获取合作限制相关实体数据失败:", error);
coopRelatedData.value = {}; coopRelatedData.value = {};
} }
}; };
// 点击跳转关联实体详情 // 点击跳转关联实体详情
const handleClickOnEntity = (item) => { const handleClickOnEntity = (item) => {
...@@ -198,7 +197,7 @@ const getCoopRestrictionIntroductionData = async () => { ...@@ -198,7 +197,7 @@ const getCoopRestrictionIntroductionData = async () => {
} catch (error) { } catch (error) {
console.error("获取合作限制简介数据失败:", error); console.error("获取合作限制简介数据失败:", error);
coopData.value = {}; coopData.value = {};
} }
}; };
// 点击跳转关联文件详情 // 点击跳转关联文件详情
...@@ -257,10 +256,11 @@ const filteredBackgroundList = computed(() => { ...@@ -257,10 +256,11 @@ const filteredBackgroundList = computed(() => {
} }
}); });
const active2 = ref("涉华背景"); // 限制条款筛选:涉华条款/全部条款
const active2 = ref("涉华条款");
const chineseNumbers = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"]; const chineseNumbers = ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十"];
const filteredClauseList = computed(() => { const filteredClauseList = computed(() => {
if (active2.value === "全部背景") { if (active2.value === "全部条款") {
return limitClauseData.value; return limitClauseData.value;
} else { } else {
return limitClauseData.value.filter(item => item.ISCN === "Y"); return limitClauseData.value.filter(item => item.ISCN === "Y");
...@@ -376,12 +376,14 @@ const dataList3 = ref([ ...@@ -376,12 +376,14 @@ const dataList3 = ref([
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.cooperation-restrictions-detail { .cooperation-restrictions-detail {
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(243, 243, 244, 1); background: rgba(243, 243, 244, 1);
overflow: auto; overflow: auto;
padding-bottom: 50px; padding-bottom: 50px;
.nav { .nav {
width: 100%; width: 100%;
height: 120px; height: 120px;
...@@ -390,52 +392,60 @@ const dataList3 = ref([ ...@@ -390,52 +392,60 @@ const dataList3 = ref([
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 99999999; z-index: 9;
.nav-main { .nav-main {
width: 1600px; width: 1600px;
height: 81px; height: 81px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
align-items: center; align-items: center;
img { img {
width: 72px; width: 72px;
height: 72px; height: 72px;
margin-right: 16px; margin-right: 16px;
} }
.content { .content {
width: 758px; width: 758px;
height: 81px; height: 81px;
margin-right: 378px; margin-right: 378px;
.cl1 { .cl1 {
font-size: 24px; font-size: 24px;
font-weight: 700; font-weight: 700;
line-height: 32px; line-height: 32px;
font-family: "Microsoft YaHei"; font-family: "Source Han Sans CN";
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
margin-bottom: 1px; margin-bottom: 1px;
} }
.cl2 { .cl2 {
height: 24px; height: 24px;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 24px; line-height: 24px;
font-family: "Microsoft YaHei"; font-family: "Source Han Sans CN";
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
margin-bottom: 1px; margin-bottom: 1px;
} }
.cl3 { .cl3 {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 24px; line-height: 24px;
font-family: "Microsoft YaHei"; font-family: "Source Han Sans CN";
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
} }
} }
.btn { .btn {
width: 376px; width: 376px;
height: 36px; height: 36px;
display: flex; display: flex;
justify-content: right; justify-content: right;
.btn1 { .btn1 {
border-radius: 6px; border-radius: 6px;
border: 1px solid rgb(230, 231, 232); border: 1px solid rgb(230, 231, 232);
...@@ -447,17 +457,20 @@ const dataList3 = ref([ ...@@ -447,17 +457,20 @@ const dataList3 = ref([
align-items: center; align-items: center;
justify-content: center; justify-content: center;
cursor: pointer; cursor: pointer;
img { img {
width: 16px; width: 16px;
height: 16px; height: 16px;
margin-right: 8px; margin-right: 8px;
} }
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
line-height: 22px; line-height: 22px;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
} }
.active { .active {
background-color: rgb(5, 95, 194); background-color: rgb(5, 95, 194);
color: #fff; color: #fff;
...@@ -465,6 +478,7 @@ const dataList3 = ref([ ...@@ -465,6 +478,7 @@ const dataList3 = ref([
} }
} }
} }
.title { .title {
width: 1600px; width: 1600px;
height: 50px; height: 50px;
...@@ -477,22 +491,29 @@ const dataList3 = ref([ ...@@ -477,22 +491,29 @@ const dataList3 = ref([
align-items: center; align-items: center;
position: relative; position: relative;
cursor: pointer; cursor: pointer;
.title-one { .title-one {
margin-left: 23px; margin-left: 23px;
font-size: 16px; font-family: "Source Han Sans CN";
font-weight: 400; font-weight: 400;
font-size: 16px;
line-height: 24px; line-height: 24px;
font-family: "Microsoft YaHei"; letter-spacing: 0px;
text-align: justify;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
} }
.title-two { .title-two {
font-size: 16px; font-family: "Source Han Sans CN";
font-weight: 700; font-weight: 700;
font-size: 16px;
line-height: 24px; line-height: 24px;
font-family: "Microsoft YaHei"; letter-spacing: 0px;
text-align: justify;
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
cursor: pointer; cursor: pointer;
} }
img { img {
width: 24px; width: 24px;
height: 24px; height: 24px;
...@@ -502,15 +523,19 @@ const dataList3 = ref([ ...@@ -502,15 +523,19 @@ const dataList3 = ref([
cursor: pointer; cursor: pointer;
} }
} }
.main { .main {
width: 1600px; width: 1600px;
height: 1373px;
margin: 0 auto; margin: 0 auto;
display: flex; display: flex;
margin-top: 16px;
.left { .left {
width: 520px; width: 520px;
height: 1012px;
margin-right: 17px; margin-right: 17px;
.left-top { .left-top {
margin-bottom: 16px; margin-bottom: 16px;
width: 520px; width: 520px;
...@@ -521,194 +546,175 @@ const dataList3 = ref([ ...@@ -521,194 +546,175 @@ const dataList3 = ref([
position: relative; position: relative;
box-sizing: border-box; box-sizing: border-box;
overflow: hidden; overflow: hidden;
padding-bottom: 33px;
.left-top-title {
font-size: 20px; .box1-main {
font-weight: 700;
line-height: 26px;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
position: absolute;
top: 14px;
left: 22px;
}
.img1 {
width: 8px;
height: 20px;
position: absolute;
left: 0px;
top: 18px;
}
.img2 {
width: 28px;
height: 28px;
position: absolute;
top: 14px;
right: 44px;
cursor: pointer;
}
.img3 {
width: 28px;
height: 28px;
position: absolute;
top: 14px;
right: 12px;
cursor: pointer;
}
.left-top-content {
width: 470px;
margin-top: 58px;
margin-left: 26px;
border-radius: 4px;
border: 1px solid rgba(231, 243, 255, 1);
background-color: rgba(246, 250, 255, 1);
display: flex; display: flex;
justify-content: center; flex-direction: column;
align-items: center; height: 100%;
padding: 16px 24px; padding-bottom: 33px;
span {
font-size: 16px; .left-top-content {
font-weight: 700; width: 470px;
line-height: 30px; margin-top: 4px;
font-family: "Microsoft YaHei"; margin-left: 26px;
color: rgb(5, 95, 194); border-radius: 4px;
} border: 1px solid rgba(231, 243, 255, 1);
} background: linear-gradient(to bottom, rgba(231, 243, 255, 1), rgba(231, 243, 255, 0));
.left-top-bottom { display: flex;
width: 460px; justify-content: center;
height: 184px; align-items: center;
margin-top: 19px; padding: 16px 24px;
margin-left: 26px;
div { span {
height: 24px;
margin-bottom: 16px;
.tit {
display: inline-block;
width: 120px;
font-size: 16px; font-size: 16px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
}
.tit1 {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 30px; line-height: 30px;
color: rgb(59, 65, 75); font-family: "Microsoft YaHei";
}
.tit2 {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
cursor: pointer;
} }
.tit3 { }
display: inline-block;
border-radius: 4px; .left-top-bottom {
background-color: rgba(231, 243, 255, 1); width: 460px;
color: rgb(5, 95, 194); height: 184px;
padding: 2px 4px; margin-top: 19px;
margin-left: 26px;
div {
height: 24px;
margin-bottom: 16px;
.tit1 {
display: inline-block;
width: 120px;
font-size: 16px;
height: 26px;
font-weight: 700;
font-family: "Source Han Sans CN";
line-height: 24px;
color: rgb(59, 65, 75);
}
.tit2 {
font-size: 16px;
font-weight: 400;
font-family: "Source Han Sans CN";
line-height: 30px;
color: rgb(59, 65, 75);
}
.tit3 {
color: rgb(5, 95, 194);
font-family: "Source Han Sans CN";
line-height: 30px;
font-size: 16px;
font-weight: 400;
cursor: pointer;
}
.tit4 {
display: inline-block;
font-family: "Source Han Sans CN";
border-radius: 4px;
line-height: 24px !important;
background-color: rgb(231, 243, 255);
color: rgb(5, 95, 194);
padding: 2px 4px;
}
} }
} }
} }
} }
.left-bottom { .left-bottom {
width: 520px; width: 520px;
height: 610px;
background-color: #fff; background-color: #fff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
position: relative;
.left-bottom-title {
font-size: 20px;
font-weight: 700;
line-height: 26px;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
position: absolute;
top: 14px;
left: 22px;
}
.img1 {
width: 8px;
height: 20px;
position: absolute;
left: 0px;
top: 18px;
}
.img2 {
width: 28px;
height: 28px;
position: absolute;
top: 14px;
right: 44px;
cursor: pointer;
}
.img3 {
width: 28px;
height: 28px;
position: absolute;
top: 14px;
right: 12px;
cursor: pointer;
}
.left-bottom-main { .left-bottom-main {
width: 478px; width: 520px;
height: 528px; padding: 6px 21px 22px 21px;
position: absolute; display: flex;
top: 60px; flex-direction: column;
left: 21px; gap: 12px;
.main-box { .main-box {
width: 480px; width: 480px;
height: 48px; height: 48px;
border-radius: 50px; border-radius: 50px;
border: 1px solid rgb(234, 236, 238); border: 1px solid rgb(234, 236, 238);
margin-bottom: 12px;
position: relative; display: flex;
padding-left: 16px;
padding-top: 12px;
padding-bottom: 12px;
background-color: rgb(247, 248, 249);
cursor: pointer; cursor: pointer;
img { img {
width: 24px; width: 24px;
height: 24px; height: 24px;
position: absolute;
top: 12px;
left: 16px;
} }
.name { .name {
position: absolute; font-family: "Source Han Sans CN";
top: 12px;
left: 52px;
font-size: 16px;
font-weight: 700; font-weight: 700;
font-family: "Microsoft YaHei"; font-size: 16px;
line-height: 24px; line-height: 24px;
letter-spacing: 0;
text-align: left;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
margin-left: 12px;
} }
.type { .type {
position: absolute; font-family: "Source Han Sans CN";
top: 12px;
right: 12px;
font-size: 16px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-size: 16px;
line-height: 24px; line-height: 24px;
letter-spacing: 0px;
text-align: right;
color: rgb(95, 101, 108); color: rgb(95, 101, 108);
margin-left: auto;
margin-right: 18px;
} }
} }
} }
} }
/* 仅本页左侧两个 AnalysisBox:头部高度改为 54px(放在 .left 作用域,避免 SCSS 嵌套导致选择器失效) */
:deep(.left-top .analysis-box-wrapper .wrapper-header),
:deep(.left-bottom .analysis-box-wrapper .wrapper-header) {
height: 54px !important;
}
:deep(.left-top .analysis-box-wrapper .wrapper-header .header-title > div),
:deep(.left-bottom .analysis-box-wrapper .wrapper-header .header-title > div) {
line-height: 54px !important;
}
} }
.right { .right {
width: 1063px; width: 1063px;
height: 1373px;
.right-top { .right-top {
margin-bottom: 16px; margin-bottom: 16px;
width: 1063px; width: 1063px;
height: 476px;
background-color: #fff; background-color: #fff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
position: relative; position: relative;
padding: 60px 19px 24px 22px; padding: 60px 19px 24px 22px;
.right-top-title { .right-top-title {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
...@@ -719,6 +725,7 @@ const dataList3 = ref([ ...@@ -719,6 +725,7 @@ const dataList3 = ref([
top: 14px; top: 14px;
left: 22px; left: 22px;
} }
.img1 { .img1 {
width: 8px; width: 8px;
height: 20px; height: 20px;
...@@ -726,9 +733,11 @@ const dataList3 = ref([ ...@@ -726,9 +733,11 @@ const dataList3 = ref([
left: 0px; left: 0px;
top: 18px; top: 18px;
} }
.right-top-content { .right-top-content {
width: 1022px; width: 1022px;
height: 392px;
.right-top-item { .right-top-item {
width: 1022px; width: 1022px;
padding: 12px 0px; padding: 12px 0px;
...@@ -737,11 +746,13 @@ const dataList3 = ref([ ...@@ -737,11 +746,13 @@ const dataList3 = ref([
align-items: center; align-items: center;
overflow: auto; overflow: auto;
} }
.right-top-item:nth-child(odd) { .right-top-item:nth-child(odd) {
background-color: rgb(247, 248, 249); background-color: rgb(247, 248, 249);
border-top: 1px solid rgb(234, 236, 238); border-top: 1px solid rgb(234, 236, 238);
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
} }
.right-top-item .id { .right-top-item .id {
display: inline-block; display: inline-block;
width: 24px; width: 24px;
...@@ -758,16 +769,20 @@ const dataList3 = ref([ ...@@ -758,16 +769,20 @@ const dataList3 = ref([
top: 16px; top: 16px;
left: 24px; left: 24px;
} }
.right-top-item .name { .right-top-item .name {
display: inline-block; display: inline-block;
width: 902px; width: 902px;
font-size: 16px; font-family: "Source Han Sans CN";
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-size: 16px;
line-height: 30px; line-height: 30px;
letter-spacing: 0px;
text-align: justify;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
margin-left: 64px; margin-left: 64px;
} }
.right-top-item img { .right-top-item img {
width: 16px; width: 16px;
height: 31px; height: 31px;
...@@ -777,6 +792,7 @@ const dataList3 = ref([ ...@@ -777,6 +792,7 @@ const dataList3 = ref([
cursor: pointer; cursor: pointer;
} }
} }
.btn { .btn {
padding: 2px 8px; padding: 2px 8px;
font-size: 16px; font-size: 16px;
...@@ -789,29 +805,36 @@ const dataList3 = ref([ ...@@ -789,29 +805,36 @@ const dataList3 = ref([
background-color: #fff; background-color: #fff;
cursor: pointer; cursor: pointer;
position: absolute; position: absolute;
z-index: 100;
} }
.cl1 { .cl1 {
top: 14px; top: 14px;
right: 107px; right: 107px;
} }
.cl2 { .cl2 {
top: 14px; top: 14px;
right: 19px; right: 19px;
} }
.active { .active {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
background-color: rgba(246, 250, 255, 1); background-color: rgba(246, 250, 255, 1);
border-color: rgb(5, 95, 194); border-color: rgb(5, 95, 194);
} }
} }
.right-bottom { .right-bottom {
width: 1063px; width: 1063px;
height: 881px;
background-color: #fff; background-color: #fff;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
position: relative; position: relative;
padding: 60px 19px 0px 22px;
.right-bottom-title { .right-bottom-title {
font-size: 20px; font-size: 20px;
font-weight: 700; font-weight: 700;
...@@ -822,6 +845,7 @@ const dataList3 = ref([ ...@@ -822,6 +845,7 @@ const dataList3 = ref([
top: 14px; top: 14px;
left: 22px; left: 22px;
} }
.img1 { .img1 {
width: 8px; width: 8px;
height: 20px; height: 20px;
...@@ -829,6 +853,7 @@ const dataList3 = ref([ ...@@ -829,6 +853,7 @@ const dataList3 = ref([
left: 0px; left: 0px;
top: 18px; top: 18px;
} }
.btn { .btn {
padding: 2px 8px; padding: 2px 8px;
font-size: 16px; font-size: 16px;
...@@ -841,40 +866,45 @@ const dataList3 = ref([ ...@@ -841,40 +866,45 @@ const dataList3 = ref([
background-color: #fff; background-color: #fff;
cursor: pointer; cursor: pointer;
position: absolute; position: absolute;
z-index: 100;
} }
.cl1 { .cl1 {
top: 14px; top: 14px;
right: 107px; right: 107px;
} }
.cl2 { .cl2 {
top: 14px; top: 14px;
right: 19px; right: 19px;
} }
.active { .active {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
background-color: rgba(246, 250, 255, 1); background-color: rgba(246, 250, 255, 1);
border-color: rgb(5, 95, 194); border-color: rgb(5, 95, 194);
} }
.right-bottom-content { .right-bottom-content {
width: 1022px; width: 1022px;
position: absolute;
top: 60px;
left: 22px;
margin-bottom: 24px;
overflow: auto; overflow: auto;
.clause-item { .clause-item {
margin-bottom: 24px; margin-bottom: 24px;
.clause-item-title { .clause-item-title {
width: 1022px; width: 1022px;
height: 55px; height: 55px;
padding: 14px 56px 17px 24px; padding: 14px 24px 17px 24px;
display: flex; display: flex;
align-items: center; align-items: center;
position: relative; justify-content: space-between;
background-color: rgb(247, 248, 249); background-color: rgb(247, 248, 249);
border-top: 1px solid rgb(234, 236, 238); border-top: 1px solid rgb(234, 236, 238);
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
span { span {
font-size: 18px; font-size: 18px;
font-weight: 700; font-weight: 700;
...@@ -882,18 +912,20 @@ const dataList3 = ref([ ...@@ -882,18 +912,20 @@ const dataList3 = ref([
line-height: 24px; line-height: 24px;
color: rgb(59, 65, 75); color: rgb(59, 65, 75);
} }
img { img {
width: 16px; width: 16px;
height: 31px; height: 31px;
position: absolute;
right: 24px; right: 24px;
top: 14px; top: 14px;
cursor: pointer; cursor: pointer;
} }
} }
.clause-item-content { .clause-item-content {
width: 1022px; width: 1022px;
padding: 12px 24px 12px 78px; padding: 12px 24px 12px 54px;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
font-family: "Microsoft YaHei"; font-family: "Microsoft YaHei";
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
<div class="main-content" ref="homeMainRef" :class="{ 'scroll-main': isShow }"> <div class="main-content" ref="homeMainRef" :class="{ 'scroll-main': isShow }">
<div class="home-top-bg"></div> <div class="home-top-bg"></div>
<!-- 搜索栏部分 --> <!-- 搜索栏部分 -->
<SearchContainer v-if="homeMainRef" placeholder="搜索合作限制" :containerRef="homeMainRef" areaName="" /> <SearchContainer v-if="homeMainRef" :countInfo="cooperationCountInfo" placeholder="搜索合作限制"
:containerRef="homeMainRef" areaName="" />
<!-- 最新动态 --> <!-- 最新动态 -->
<div class="newdata" id="position1"> <div class="newdata" id="position1">
...@@ -57,9 +58,36 @@ import newData from "./components/dataNew/index.vue"; ...@@ -57,9 +58,36 @@ import newData from "./components/dataNew/index.vue";
import askPage from "./components/askPage/index.vue"; import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue"; import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue"; import resLib from "./components/resLib/index.vue";
import { getCoopRestrictionStatistics } from "@/api/coopRestriction/coopRestriction.js";
import { useContainerScroll } from "@/hooks/useScrollShow"; import { useContainerScroll } from "@/hooks/useScrollShow";
const cooperationCountInfo = ref([]);
// const
const getCooperationCountInfo = async () => {
try {
const res = await getCoopRestrictionStatistics();
if (res && res.code === 200) {
// console.log('----getStatCountInfo', res.data)
cooperationCountInfo.value = [
{
name: "相关法案",
count: res.data.billCounts
},
{
name: "相关政令",
count: res.data.aocounts
},
{
name: "相关政府公告",
count: res.data.ggcounts
},
];
}
} catch (error) {
console.error("获取首页统计接口失败:", error);
}
};
// 搜索框 // 搜索框
const input = ref(""); const input = ref("");
const homeMainRef = ref(null); const homeMainRef = ref(null);
...@@ -71,7 +99,9 @@ const router = useRouter(); ...@@ -71,7 +99,9 @@ const router = useRouter();
const handleSearch = () => { const handleSearch = () => {
console.log("搜索内容:", input.value); console.log("搜索内容:", input.value);
}; };
onMounted(() => {
getCooperationCountInfo();
});
// 锚点跳转 // 锚点跳转
const handleToPosi = id => { const handleToPosi = id => {
const element = document.getElementById(id); const element = document.getElementById(id);
...@@ -106,6 +136,7 @@ const handleToPosi = id => { ...@@ -106,6 +136,7 @@ const handleToPosi = id => {
.coop-page { .coop-page {
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
// .breadcrumb { // .breadcrumb {
// width: 100%; // width: 100%;
// height: 64px; // height: 64px;
...@@ -138,6 +169,12 @@ const handleToPosi = id => { ...@@ -138,6 +169,12 @@ const handleToPosi = id => {
top: -64px; top: -64px;
} }
:deep(.search-container .search-center) {
width: 440px;
gap: 40px;
justify-content: center;
}
.search { .search {
width: 960px; width: 960px;
height: 168px; height: 168px;
...@@ -467,8 +504,6 @@ const handleToPosi = id => { ...@@ -467,8 +504,6 @@ const handleToPosi = id => {
} }
} }
.scroll-main {
height: calc(100% - 144px) !important;
}
} }
</style> </style>
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
</div> </div>
<div class="title">{{ "最新科技政令" }}</div> <div class="title">{{ "最新科技政令" }}</div>
</div> </div>
<div class="box1-header-right" @click="handleClickToDetail"> <div class="box1-header-right" @click="handleClickOrder">
{{ "查看详情 >" }} {{ "查看详情 >" }}
</div> </div>
</div> </div>
...@@ -116,48 +116,7 @@ ...@@ -116,48 +116,7 @@
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
</div> </div>
<!-- <div class="box2"> <RiskSignal :list="warningList" @item-click="onNavigateToDetail" @more-click="handleToMoreRiskSignal"
<div class="box2-header">
<div class="icon">
<img src="./assets/images/box2-header-icon.png" alt="" />
</div>
<div class="title">
<div class="text">{{ "风险信号" }}</div>
<div class="num">{{ warningList.length }}</div>
</div>
</div>
<div class="box2-main">
<div
class="box2-main-item"
v-for="(item, index) in warningList"
:key="index"
@click="handleClickToDetail()"
>
<div
class="item-left"
:class="{
itemLeftStatus3: item.riskLevel === '特别重大',
itemLeftStatus2: item.riskLevel === '重大风险'
}"
>
{{ item.riskLevel ? item.riskLevel : "暂无数据" }}
</div>
<div class="item-right">
<div class="text">
{{ item.name }}
</div>
<div class="time">{{ item.postDate }}</div>
</div>
</div>
</div>
<div class="box2-footer" @click="handleToMoreRiskSignal">
<div class="icon">
<img src="./assets/images/box2-footer-icon.png" alt="" />
</div>
<div class="text">{{ "查看更多" }}</div>
</div>
</div> -->
<RiskSignal :list="warningList" @item-click="handleClickToDetail" @more-click="handleToMoreRiskSignal"
riskLevel="signalLevel" postDate="signalTime" name="signalTitle"> riskLevel="signalLevel" postDate="signalTime" name="signalTitle">
</RiskSignal> </RiskSignal>
</div> </div>
...@@ -268,7 +227,7 @@ ...@@ -268,7 +227,7 @@
</div> </div>
<div class="box7-main" v-loading="box7Params.loading"> <div class="box7-main" v-loading="box7Params.loading">
<div class="box7-list"> <div class="box7-list">
<div class="box7-item" v-for="(item, index) in keyDecreeList" :key="index" @click="handleKeyDecree(item)"> <div class="box7-item" v-for="(item, index) in keyDecreeList" :key="index" @click="onNavigateToDetail(item)">
<div class="icon"> <div class="icon">
<img src="./assets/images/warning.png" alt="" /> <img src="./assets/images/warning.png" alt="" />
</div> </div>
...@@ -408,7 +367,7 @@ ...@@ -408,7 +367,7 @@
<div class="title">{{ "政令库" }}</div> <div class="title">{{ "政令库" }}</div>
</div> </div>
<div class="content-box" v-show="decreeList"> <div class="content-box" v-show="decreeList">
<div class="main-item" v-for="(item, index) in decreeList" :key="index" @click="handleClickDecree(item)"> <div class="main-item" v-for="(item, index) in decreeList" :key="index" @click="onNavigateToDetail(item)">
<div class="main-item-left"> <div class="main-item-left">
<div class="left-left"> <div class="left-left">
{{ item.time?.split("-")[0] }}<br />{{ item.time?.split("-")[1] }}月{{ {{ item.time?.split("-")[0] }}<br />{{ item.time?.split("-")[1] }}月{{
...@@ -575,69 +534,28 @@ const handleGetLatestDecree = async () => { ...@@ -575,69 +534,28 @@ const handleGetLatestDecree = async () => {
const handleBox1 = async () => { const handleBox1 = async () => {
await handleGetLatestDecree(); await handleGetLatestDecree();
}; };
const carouselRef = ref(null);
// 点击查看详情
const handleClickToDetail = () => {
let activeIndex = 0;
if (carouselRef.value) {
activeIndex = carouselRef.value.activeIndex;
}
console.log("当前 Carousel 激活索引:", activeIndex);
const id = box1DataList.value[activeIndex].id; const carouselRef = ref(null);
const handleClickOrder = () => {
window.sessionStorage.setItem("curTabName", box1DataList.value[activeIndex].name); let index = carouselRef.value?.activeIndex || 0;
const route = router.resolve({ let item = box1DataList.value[index];
path: "/decreeLayout", console.log(item)
query: { if (item) onNavigateToDetail(item)
id: id
}
});
window.open(route.href, "_blank");
// router.push({
// path: "/decreeLayout",
// query: {
// id: id
// }
// })
};
// 点击政令库政令
const handleClickDecree = decree => {
window.sessionStorage.setItem("curTabName", decree.title);
const route = router.resolve({
path: "/decreeLayout",
query: {
id: decree.id
}
});
window.open(route.href, "_blank");
// router.push({
// path: "/decreeLayout",
// query: {
// id: decree.id
// }
// })
}; };
const handleKeyDecree = item => { // 跳转政令详情
window.sessionStorage.setItem("curTabName", item.title); const onNavigateToDetail = (item) => {
window.sessionStorage.setItem("curTabName", item.name || item.title || item.signalTitle);
const route = router.resolve({ const route = router.resolve({
path: "/decreeLayout", path: "/decreeLayout",
query: { query: { id: item.orderId || item.id }
id: item.id
}
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
// router.push({ // router.push({
// path: "/decreeLayout", // path: "/decreeLayout",
// query: { // query: { id: item.orderId || item.id }
// id: item.id
// }
// }) // })
}; }
// 风险信号 // 风险信号
const warningList = ref([ const warningList = ref([
...@@ -1030,6 +948,7 @@ const box8Params = reactive({ ...@@ -1030,6 +948,7 @@ const box8Params = reactive({
}) })
const handleGetDecreeKeyInstruction = async () => { const handleGetDecreeKeyInstruction = async () => {
box8Params.loading = true box8Params.loading = true
wordCloudData.value = []
try { try {
let { year, domainId, proposeName } = box8Params; let { year, domainId, proposeName } = box8Params;
const res = await getDecreeKeyInstruction({ const res = await getDecreeKeyInstruction({
...@@ -1038,8 +957,13 @@ const handleGetDecreeKeyInstruction = async () => { ...@@ -1038,8 +957,13 @@ const handleGetDecreeKeyInstruction = async () => {
orgId: proposeName || undefined orgId: proposeName || undefined
}); });
console.log("政令重点条款", res); console.log("政令重点条款", res);
wordCloudData.value = res.data.map(item => ({name: item.clause, value: item.count})); if (res.code==200) {
wordCloudData.value = res.data.map(item => ({name: item.clause, value: item.count}));
} else {
wordCloudData.value = []
}
} catch (error) { } catch (error) {
wordCloudData.value = []
console.error("政令重点条款error", error); console.error("政令重点条款error", error);
} }
box8Params.loading = false box8Params.loading = false
...@@ -1330,8 +1254,7 @@ onMounted(async () => { ...@@ -1330,8 +1254,7 @@ onMounted(async () => {
width: 100%; width: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; padding: 16px 22px;
padding: 16px 0;
.data-origin-icon { .data-origin-icon {
width: 16px; width: 16px;
height: 16px; height: 16px;
...@@ -1833,174 +1756,6 @@ onMounted(async () => { ...@@ -1833,174 +1756,6 @@ onMounted(async () => {
} }
} }
} }
.box2 {
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);
position: relative;
.box2-header {
height: 48px;
display: flex;
border-bottom: 1px solid rgba(240, 242, 244, 1);
.icon {
width: 24px;
height: 22px;
margin-left: 18px;
margin-top: 14px;
img {
width: 100%;
height: 100%;
}
}
.title {
display: flex;
width: 148px;
background: rgba(206, 79, 81, 1);
margin-left: 18px;
.text {
margin-left: 16px;
height: 48px;
color: #fff;
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 48px;
}
.num {
width: 24px;
height: 20px;
line-height: 20px;
text-align: center;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 12px;
margin-left: 15px;
margin-top: 15px;
border-radius: 100px;
background: rgba(255, 255, 255, 0.3);
}
}
}
.box2-main {
box-sizing: border-box;
padding-right: 20px;
height: 282px;
overflow-y: auto;
.box2-main-item {
margin-left: 23px;
height: 47px;
width: 464px;
display: flex;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.itemLeftStatus3 {
color: rgba(245, 34, 45, 1) !important;
background: rgba(255, 241, 240) !important;
}
.itemLeftStatus2 {
color: rgba(250, 140, 22, 1) !important;
background: rgba(255, 247, 230, 1) !important;
}
.item-left {
margin-top: 4px;
margin-left: 2px;
width: 40px;
height: 40px;
border-radius: 20px;
color: rgba(82, 196, 26, 1);
background: rgba(246, 255, 237, 1);
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: 400;
line-height: 14px;
box-sizing: border-box;
padding: 6px 4px;
text-align: center;
}
.item-right {
margin-left: 13px;
width: 408px;
height: 47px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
display: flex;
.text {
width: 318px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 47px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time {
width: 90px;
line-height: 47px;
text-align: center;
color: rgba(132, 136, 142, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
}
}
}
}
.box2-footer {
position: absolute;
left: 45px;
bottom: 20px;
width: 430px;
height: 42px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
border-radius: 6px;
background: var(--color-main-active);
cursor: pointer;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
margin-left: 8px;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
}
}
}
} }
.divide2 { .divide2 {
...@@ -2286,7 +2041,7 @@ onMounted(async () => { ...@@ -2286,7 +2041,7 @@ onMounted(async () => {
width: 792px; width: 792px;
height: 100%; height: 100%;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
...@@ -2402,7 +2157,7 @@ onMounted(async () => { ...@@ -2402,7 +2157,7 @@ onMounted(async () => {
width: 792px; width: 792px;
height: 100%; height: 100%;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(60, 87, 126, 0.2); box-shadow: 0px 0px 15px 0px rgba(22, 119, 255, 0.1);
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="wrap"> <div class="wrap">
<div class="box1"> <div class="box1">
<AnalysisBox title="相关政令" :showAllBtn="false"> <AnalysisBox title="相关政令" :showAllBtn="false">
<div class="box1-main"> <div class="box1-main" v-loading="isLoading">
<el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" /> <el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<el-scrollbar height="100%" always> <el-scrollbar height="100%" always>
<div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickDecree(item)"> <div class="left-item" :class="{ 'item-active': false }" v-for="(item, index) in siderList" :key="index" @click="handleClickDecree(item)">
...@@ -18,9 +18,11 @@ ...@@ -18,9 +18,11 @@
</div> </div>
<div class="box2"> <div class="box2">
<AnalysisBox title="政令关系挖掘" :showAllBtn="false"> <AnalysisBox title="政令关系挖掘" :showAllBtn="false">
<el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" /> <div style="height: 100%; width: 100%;" v-loading="isLoading">
<div class="box2-main" v-if="graphData.nodes?.length"> <el-empty v-if="!siderList?.length" style="padding: 60px 0;" description="暂无数据" :image-size="100" />
<GraphChart :nodes="graphData.nodes" :links="graphData.links" layoutType="force" @handleClickNode="handleClickNode" /> <div class="box2-main" v-if="graphData.nodes?.length">
<GraphChart :nodes="graphData.nodes" :links="graphData.links" layoutType="force" @handleClickNode="handleClickNode" />
</div>
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -28,7 +30,7 @@ ...@@ -28,7 +30,7 @@
<el-dialog v-model="dialogVisible" width="1000px" class="viewpoint-dialog"> <el-dialog v-model="dialogVisible" width="1000px" class="viewpoint-dialog">
<template #header> <template #header>
<div class="viewpoint-header"> <div class="viewpoint-header">
<div class="viewpoint-title">冲突关系</div> <div class="viewpoint-title">关联关系</div>
</div> </div>
</template> </template>
<div class="viewpoint-body"> <div class="viewpoint-body">
...@@ -61,7 +63,7 @@ import icon1629 from "./assets/icons/icon1629.png"; ...@@ -61,7 +63,7 @@ import icon1629 from "./assets/icons/icon1629.png";
const route = useRoute(); const route = useRoute();
// 冲突关系 // 关联关系
const dialogVisible = ref(false); const dialogVisible = ref(false);
// 基本信息 // 基本信息
...@@ -123,13 +125,16 @@ const handleClickNode = ({data}) => { ...@@ -123,13 +125,16 @@ const handleClickNode = ({data}) => {
if (node) handleClickDecree(node) if (node) handleClickDecree(node)
} }
} }
const isLoading = ref(false);
const initGraphChart = () => { const initGraphChart = () => {
isLoading.value = true;
Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => { Promise.all([onDecreeSummaryData(), handleGetRelateOrder()]).then(() => {
if (mainInfo.value.id && siderList.value.length) { if (mainInfo.value.id && siderList.value.length) {
graphData.links = siderList.value.map(onFormatLink) graphData.links = siderList.value.map(onFormatLink)
graphData.nodes = siderList.value.map(onFormatNode) graphData.nodes = siderList.value.map(onFormatNode)
graphData.nodes.unshift(onFormatNode(mainInfo.value)) graphData.nodes.unshift(onFormatNode(mainInfo.value))
} }
isLoading.value = false;
}) })
} }
const onFormatLink = (item, index) => { const onFormatLink = (item, index) => {
...@@ -185,7 +190,7 @@ const handleClickDecree = decree => { ...@@ -185,7 +190,7 @@ const handleClickDecree = decree => {
// }) // })
}; };
// 冲突关系 // 关联关系
const graphContainer = ref(null); const graphContainer = ref(null);
let graph = null; let graph = null;
const onRelationChart = () => { const onRelationChart = () => {
......
...@@ -183,7 +183,7 @@ const headerChartData = (row) => { ...@@ -183,7 +183,7 @@ const headerChartData = (row) => {
switch (contentType.value) { switch (contentType.value) {
case 1: case 1:
onDecreeRelatedChain(row.id) onDecreeRelatedChain(row.orgId)
break; break;
case 2: case 2:
onDecreeRelatedEntitie(row.orgId) onDecreeRelatedEntitie(row.orgId)
......
...@@ -384,7 +384,7 @@ ...@@ -384,7 +384,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="radarChart.loading ? '解读生成中...' : radarChart.interpretation" /> <AiPane :aiContent="radarChart.interpretation" />
</div> </div>
</template> </template>
</custom-container> </custom-container>
...@@ -409,7 +409,7 @@ ...@@ -409,7 +409,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="trendChart.loading ? '解读生成中...' : trendChart.interpretation" /> <AiPane :aiContent="trendChart.interpretation" />
</div> </div>
</template> </template>
</custom-container> </custom-container>
...@@ -830,7 +830,6 @@ const handleToRiskSignalDetail = item => { ...@@ -830,7 +830,6 @@ const handleToRiskSignalDetail = item => {
}); });
// 打开新页面 // 打开新页面
window.open(routeData.href, "_blank"); window.open(routeData.href, "_blank");
}; };
const sanctionList = ref([]); const sanctionList = ref([]);
...@@ -1042,7 +1041,6 @@ const handleToSocialDetail = item => { ...@@ -1042,7 +1041,6 @@ const handleToSocialDetail = item => {
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
// 获取趋势图数据 // 获取趋势图数据
const fetchTrendData = async () => { const fetchTrendData = async () => {
...@@ -1180,7 +1178,6 @@ const handleToEntityListNoId = item => { ...@@ -1180,7 +1178,6 @@ const handleToEntityListNoId = item => {
}); });
// 打开一个新页面 // 打开一个新页面
window.open(routeData.href, "_blank"); window.open(routeData.href, "_blank");
} else if (item.nameZh == "商业管制清单") { } else if (item.nameZh == "商业管制清单") {
const routeData = router.resolve({ const routeData = router.resolve({
path: "/exportControl/commercialControlList", path: "/exportControl/commercialControlList",
...@@ -1195,8 +1192,8 @@ const handleToEntityListNoId = item => { ...@@ -1195,8 +1192,8 @@ const handleToEntityListNoId = item => {
} }
}; };
const billList = ref([]); // const billList = ref([]);
const curBillListIndex = ref(0); // const curBillListIndex = ref(0);
const searchExportControlText = ref(""); const searchExportControlText = ref("");
const infoListColor = ref(["rgba(206, 79, 81, 1)", "rgba(114, 46, 209, 1)", "rgba(132, 136, 142, 1)", "rgba(132, 136, 142, 1)"]); const infoListColor = ref(["rgba(206, 79, 81, 1)", "rgba(114, 46, 209, 1)", "rgba(132, 136, 142, 1)", "rgba(132, 136, 142, 1)"]);
...@@ -1391,8 +1388,8 @@ const fetchSanctionList = async () => { ...@@ -1391,8 +1388,8 @@ const fetchSanctionList = async () => {
const tags = Array.isArray(item.techDomains) const tags = Array.isArray(item.techDomains)
? item.techDomains ? item.techDomains
: item.techDomain : item.techDomain
? [item.techDomain] ? [item.techDomain]
: item.techDomainList || []; : item.techDomainList || [];
const fullTime = item.startTime const fullTime = item.startTime
? formatAnyDateToChinese(item.startTime) ? formatAnyDateToChinese(item.startTime)
...@@ -1422,8 +1419,8 @@ const fetchSanctionList = async () => { ...@@ -1422,8 +1419,8 @@ const fetchSanctionList = async () => {
countTag: item.cnEntityCount countTag: item.cnEntityCount
? `${item.cnEntityCount}家中国实体` ? `${item.cnEntityCount}家中国实体`
: item.ruleOrgCount : item.ruleOrgCount
? `${item.ruleOrgCount}家关联实体` ? `${item.ruleOrgCount}家关联实体`
: item.countTag || "" : item.countTag || ""
}; };
}); });
totalAll.value = res.totalElements; totalAll.value = res.totalElements;
...@@ -1666,12 +1663,11 @@ const handlePerClick = item => { ...@@ -1666,12 +1663,11 @@ const handlePerClick = item => {
const route = router.resolve({ const route = router.resolve({
path: "/characterPage", path: "/characterPage",
query: { query: {
type: item.type || [1, 2, 3][Math.floor(Math.random() * 3)], type: item.personType || [1, 2, 3][Math.floor(Math.random() * 3)],
personId: item.personId personId: item.personId
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
// 处理点击社交媒体消息的方法 // 处理点击社交媒体消息的方法
// const handleInfoClick = item => { // const handleInfoClick = item => {
...@@ -1751,17 +1747,16 @@ const chart1Data = ref({ ...@@ -1751,17 +1747,16 @@ const chart1Data = ref({
}); });
const handleSanc = item => { const handleSanc = item => {
console.log(item); console.log("activeResourceTabItem.value.id", activeResourceTabItem.value.id);
window.sessionStorage.setItem("curTabName", `${item.postDate}${item.title}》`); window.sessionStorage.setItem("curTabName", `${item.postDate}${item.title}》`);
const route = router.resolve({ const route = router.resolve({
path: "/exportControl/singleSanction", path: "/exportControl/singleSanction",
query: { query: {
id: item.id, id: item.id,
sanTypeId: activeResourceTabItem.value.id sanTypeId: activeResourceTabItem.value.id.join(",")
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
// 查看更多风险信号 // 查看更多风险信号
...@@ -2368,8 +2363,7 @@ const handleMediaClick = item => { ...@@ -2368,8 +2363,7 @@ const handleMediaClick = item => {
overflow-y: auto; overflow-y: auto;
.home-top-bg { .home-top-bg {
background: background: url("./assets/images/background.png"),
url("./assets/images/background.png"),
linear-gradient(180deg, rgba(229, 241, 254, 1) 0%, rgba(246, 251, 255, 0) 30%); linear-gradient(180deg, rgba(229, 241, 254, 1) 0%, rgba(246, 251, 255, 0) 30%);
background-size: 100% 100%; background-size: 100% 100%;
position: absolute; position: absolute;
......
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
<div class="list-content" v-for="(ele, j) in element.cclChildren" :key="j"> <div class="list-content" v-for="(ele, j) in element.cclChildren" :key="j">
<div class="content-title" @click="ele.isExpand = !ele.isExpand"> <div class="content-title" @click="ele.isExpand = !ele.isExpand">
<div class="code-zone"> <div class="code-zone">
<!-- <div class="dot" v-if="ele.isDot"></div> --> <div class="dot" v-if="ele.changeFlag"></div>
<span class="code">{{ ele.cclCode }}</span> <span class="code">{{ ele.cclCode }}</span>
</div> </div>
<span class="name">{{ ele.cclTitleZh }}</span> <span class="name">{{ ele.cclTitleZh }}</span>
...@@ -121,9 +121,9 @@ const getTechFields = async () => { ...@@ -121,9 +121,9 @@ const getTechFields = async () => {
const res = await getAreaType(); const res = await getAreaType();
if (res && res.code === 200) { if (res && res.code === 200) {
console.log("-----getTechFields", res.data); console.log("-----getTechFields", res.data);
techFields.value = res.data; techFields.value = [{ id: "all", name: "全部领域", checked: true }].concat(res.data);
// 默认选中第一个 // 默认选中第一个
techFields.value[0].checked = true; // techFields.value[0].checked = true;
} }
} catch (error) { } catch (error) {
console.error("获取科技领域字典失败:", error); console.error("获取科技领域字典失败:", error);
...@@ -136,9 +136,9 @@ const getControlReasonList = async () => { ...@@ -136,9 +136,9 @@ const getControlReasonList = async () => {
const res = await getControlReason(); const res = await getControlReason();
if (res && res.code === 200) { if (res && res.code === 200) {
console.log("-----getControlReasonList", res.data); console.log("-----getControlReasonList", res.data);
controlReason.value = res.data; controlReason.value = [{ id: "all", name: "全部", checked: true }].concat(res.data);
// 默认选中第一个 // 默认选中第一个
controlReason.value[0].checked = true; // controlReason.value[0].checked = true;
} }
} catch (error) { } catch (error) {
console.error("获取管控原因字典失败:", error); console.error("获取管控原因字典失败:", error);
...@@ -151,8 +151,19 @@ const viewNew = ref(true); ...@@ -151,8 +151,19 @@ const viewNew = ref(true);
// 获取ccl清单列表 // 获取ccl清单列表
const getCclList = async () => { const getCclList = async () => {
let techDomains = techFields.value.filter(item => item.checked).map(item => +item.id); let techDomains = [];
let controls = controlReason.value.filter(item => item.checked).map(item => +item.id); const allTech = techFields.value.find(item => item.id === "all"); // ✅ 修正点1
if (!allTech || !allTech.checked) {
techDomains = techFields.value.filter(item => item.checked && item.id !== "all").map(item => item.id); // 注意:这里 map 的是 value,假设后端需要的是 value 字段
}
let controls = [];
const allReason = controlReason.value.find(item => item.id === "all"); // ✅ 修正点2
if (!allReason || !allReason.checked) {
controls = controlReason.value.filter(item => item.checked && item.id !== "all").map(item => item.id);
}
// let techDomains = techFields.value.filter(item => item.checked).map(item => +item.id);
// let controls = controlReason.value.filter(item => item.checked).map(item => +item.id);
const params = { const params = {
categoryCode: currentCCLType.value, categoryCode: currentCCLType.value,
techDomainIds: techDomains, techDomainIds: techDomains,
...@@ -161,7 +172,7 @@ const getCclList = async () => { ...@@ -161,7 +172,7 @@ const getCclList = async () => {
isLatest: viewNew.value, isLatest: viewNew.value,
recordId: currentCCLVersion.value || "" recordId: currentCCLVersion.value || ""
}; };
console.log(JSON.stringify(params)); console.log("cclList参数:", params);
try { try {
// const res = await getCclQuery(null); // const res = await getCclQuery(null);
const res = await getCclQuery(params); const res = await getCclQuery(params);
...@@ -272,8 +283,34 @@ const getCCLVersionListApi = async () => { ...@@ -272,8 +283,34 @@ const getCCLVersionListApi = async () => {
}; };
// 筛选逻辑处理 // 筛选逻辑处理
const handleFilterChange = (item, list, type) => { const handleFilterChange = (item, listRef, type) => {
console.log(item, list, type); const list = listRef;
const allItem = list.find(i => i.id === "all"); // ✅ 关键修正
if (!allItem) return;
if (item.id === "all") {
if (item.checked) {
list.forEach(i => {
if (i.id !== "all") i.checked = false;
});
} else {
const hasOtherChecked = list.some(i => i.id !== "all" && i.checked);
if (!hasOtherChecked && list.length > 1) {
const firstNonAll = list.find(i => i.id !== "all");
if (firstNonAll) firstNonAll.checked = true;
}
}
} else {
if (item.checked) {
allItem.checked = false;
} else {
const hasAnyChecked = list.some(i => i.id !== "all" && i.checked);
if (!hasAnyChecked) {
allItem.checked = true;
}
}
}
getCclList(); getCclList();
}; };
...@@ -373,6 +410,9 @@ onMounted(async () => { ...@@ -373,6 +410,9 @@ onMounted(async () => {
:deep(.el-input__wrapper) { :deep(.el-input__wrapper) {
padding: 0 11px; padding: 0 11px;
border: 1px solid rgba(0, 0, 0, 0.1);
background-color: #fff;
border-radius: 3px;
} }
:deep(.el-input__inner) { :deep(.el-input__inner) {
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
</div> </div>
<div class="department">{{ headerTitle.department }}</div> <div class="department">{{ headerTitle.department }}</div>
</div> </div>
<div class="btn"><img :src="icon01" alt />切换</div> <!-- <div class="btn"><img :src="icon01" alt />切换</div> -->
</div> </div>
<div class="header-nav"> <div class="header-nav">
<div <div
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="sanctionCountChart.loading ? '解读生成中...' : sanctionCountChart.interpretation" /> <AiPane :aiContent="sanctionCountChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="rankChart.loading ? '解读生成中...' : rankChart.interpretation" /> <AiPane :aiContent="rankChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="domainChart.loading ? '解读生成中...' : domainChart.interpretation" /> <AiPane :aiContent="domainChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -238,7 +238,7 @@ ...@@ -238,7 +238,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="typeChart.loading ? '解读生成中...' : typeChart.interpretation" /> <AiPane :aiContent="typeChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
......
...@@ -532,6 +532,7 @@ watch(customDateRange, () => { ...@@ -532,6 +532,7 @@ watch(customDateRange, () => {
padding: 0 11px; padding: 0 11px;
border: 1px solid rgba(170, 173, 177, 1); border: 1px solid rgba(170, 173, 177, 1);
background-color: #fff; background-color: #fff;
border-radius: 3px;
} }
:deep(.el-input__inner) { :deep(.el-input__inner) {
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="domainChart.loading ? '解读生成中...' : domainChart.interpretation" /> <AiPane :aiContent="domainChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -113,7 +113,7 @@ ...@@ -113,7 +113,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="typeChart.loading ? '解读生成中...' : typeChart.interpretation" /> <AiPane :aiContent="typeChart.interpretation" />
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -154,11 +154,7 @@ ...@@ -154,11 +154,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="countryDistributionChart.interpretation" />
:aiContent="
countryDistributionChart.loading ? '解读生成中...' : countryDistributionChart.interpretation
"
/>
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -202,11 +198,7 @@ ...@@ -202,11 +198,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="regionDistributionChart.interpretation" />
:aiContent="
regionDistributionChart.loading ? '解读生成中...' : regionDistributionChart.interpretation
"
/>
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
......
...@@ -146,7 +146,7 @@ ...@@ -146,7 +146,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="revenueChart.loading ? '解读生成中...' : revenueChart.interpretation" /> <AiPane :aiContent="revenueChart.interpretation" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -188,7 +188,7 @@ ...@@ -188,7 +188,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="marketChart.loading ? '解读生成中...' : marketChart.interpretation" /> <AiPane :aiContent="marketChart.interpretation" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="rdChart.loading ? '解读生成中...' : rdChart.interpretation" /> <AiPane :aiContent="rdChart.interpretation" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -310,7 +310,7 @@ ...@@ -310,7 +310,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane :aiContent="shareChart.loading ? '解读生成中...' : shareChart.interpretation" /> <AiPane :aiContent="shareChart.interpretation" />
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
......
...@@ -60,13 +60,7 @@ ...@@ -60,13 +60,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="rdInstrumentDependencyChart.interpretation" />
:aiContent="
rdInstrumentDependencyChart.loading
? '解读生成中...'
: rdInstrumentDependencyChart.interpretation
"
/>
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -102,13 +96,7 @@ ...@@ -102,13 +96,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="rdInstrumentImportCountryChart.interpretation" />
:aiContent="
rdInstrumentImportCountryChart.loading
? '解读生成中...'
: rdInstrumentImportCountryChart.interpretation
"
/>
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -177,13 +165,7 @@ ...@@ -177,13 +165,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="internationalCooperationChart.interpretation" />
:aiContent="
internationalCooperationChart.loading
? '解读生成中...'
: internationalCooperationChart.interpretation
"
/>
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
...@@ -252,11 +234,7 @@ ...@@ -252,11 +234,7 @@
</div> </div>
<div class="ai-pane"> <div class="ai-pane">
<AiButton /> <AiButton />
<AiPane <AiPane :aiContent="internationalPaperChart.interpretation" />
:aiContent="
internationalPaperChart.loading ? '解读生成中...' : internationalPaperChart.interpretation
"
/>
</div> </div>
</div> </div>
</AnalysisBox> </AnalysisBox>
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
<div class="info-row"> <div class="info-row">
<div class="label">发布人:</div> <div class="label">发布人:</div>
<div class="value link"> <div class="value link">
<img :src="defaultTitle" alt="" class="icon avatar" /> <img :src="formattedData.avartar || defaultTitle" alt="" class="icon avatar" />
<span @click="handleClick">{{ formattedData.postPersonName }} ></span> <span @click="handleClick">{{ formattedData.postPersonName }} ></span>
</div> </div>
</div> </div>
...@@ -46,13 +46,16 @@ ...@@ -46,13 +46,16 @@
<div class="content-title">制裁实体分布:</div> <div class="content-title">制裁实体分布:</div>
<div class="distribution-list"> <div class="distribution-list">
<div class="list-item" v-for="(item, index) in entityDistribution" :key="index"> <div class="list-item" v-for="(item, index) in entityDistribution" :key="index">
<img :src="flag" alt="" class="flag" /> <img :src="item.imageUrl || flag" alt="" class="flag" />
<div class="country-name">{{ item.name }}</div> <div class="country-name">{{ item.name }}</div>
<div class="progress-bar-container"> <div class="progress-bar-container">
<div class="progress-bar" :style="{ <div
width: item.width, class="progress-bar"
background: item.gradient :style="{
}"></div> width: item.width,
background: item.gradient
}"
></div>
</div> </div>
<div class="count" :class="{ highlight: index === 0 }">{{ item.count }}</div> <div class="count" :class="{ highlight: index === 0 }">{{ item.count }}</div>
</div> </div>
...@@ -95,13 +98,25 @@ ...@@ -95,13 +98,25 @@
</div> </div>
<div class="filter-right"> <div class="filter-right">
<el-checkbox v-model="onlyChina" label="只看中国实体" /> <el-checkbox v-model="onlyChina" label="只看中国实体" />
<el-select v-model="filterField" placeholder="全部领域" style="width: 150px; margin: 0 12px 0 16px"> <el-select
v-model="filterField"
placeholder="全部领域"
style="width: 150px; margin: 0 12px 0 16px"
>
<el-option label="全部领域" value="" /> <el-option label="全部领域" value="" />
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" /> <el-option
v-for="item in domainOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
<el-input v-model="searchKeyword" placeholder="搜索实体" <el-input
v-model="searchKeyword"
placeholder="搜索实体"
style="width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px" style="width: 150px; border: 1px solid rgba(170, 173, 177, 0.4); border-radius: 5px"
:suffix-icon="Search" /> :suffix-icon="Search"
/>
</div> </div>
</div> </div>
<div class="stats-row"> <div class="stats-row">
...@@ -116,20 +131,28 @@ ...@@ -116,20 +131,28 @@
<div class="stats-info"> <div class="stats-info">
<div class="stat-item"> <div class="stat-item">
<span class="dot red"></span> <span class="dot red"></span>
<span class="text">新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{ <span class="text"
addRuleCount >新增 <span class="num red">{{ addCount }}</span> 家 (50%规则涉及<span class="num red">{{
}}</span>家)</span> addRuleCount
}}</span
>家)</span
>
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="dot green"></span> <span class="dot green"></span>
<span class="text">移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span <span class="text"
class="num green">{{ removeRuleCount }}</span>家)</span> >移除 <span class="num green">{{ removeCount }}</span> 家 (50%规则涉及<span
class="num green"
>{{ removeRuleCount }}</span
>家)</span
>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="right-content"> <div class="right-content">
<div class="sanction-group-list"> <div class="sanction-group-list">
<el-empty v-if="sanctionList.length === 0" description="暂无制裁清单" />
<div class="sanction-group" v-for="(group, index) in sanctionList" :key="index"> <div class="sanction-group" v-for="(group, index) in sanctionList" :key="index">
<el-table :data="group.entities" style="width: 100%"> <el-table :data="group.entities" style="width: 100%">
<el-table-column label="实体名称" min-width="280"> <el-table-column label="实体名称" min-width="280">
...@@ -145,8 +168,14 @@ ...@@ -145,8 +168,14 @@
</el-table-column> </el-table-column>
<el-table-column label="涉及领域" width="180" align="center"> <el-table-column label="涉及领域" width="180" align="center">
<template #default="scope"> <template #default="scope">
<span v-for="(item, index) in scope.row.fields" :key="index" class="tag" <span
:style="getTagStyle(item)" style="margin: 0 2px">{{ item }}</span> v-for="(item, index) in scope.row.fields"
:key="index"
class="tag"
:style="getTagStyle(item)"
style="margin: 0 2px"
>{{ item }}</span
>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="location" label="上市地点" width="90" align="center" /> <el-table-column prop="location" label="上市地点" width="90" align="center" />
...@@ -154,8 +183,11 @@ ...@@ -154,8 +183,11 @@
<el-table-column prop="revenue" label="营收(亿元)" width="110" align="center" /> <el-table-column prop="revenue" label="营收(亿元)" width="110" align="center" />
<el-table-column label="50%规则子企业" width="180" align="center"> <el-table-column label="50%规则子企业" width="180" align="center">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.subsidiaryCount" class="subsidiary-link" <span
@click="handleSubsidiaryClick(scope.row)"> v-if="scope.row.subsidiaryCount"
class="subsidiary-link"
@click="handleSubsidiaryClick(scope.row)"
>
{{ scope.row.subsidiaryText }} {{ scope.row.subsidiaryText }}
<span class="blue-text">{{ scope.row.subsidiaryCount }}家 ></span> <span class="blue-text">{{ scope.row.subsidiaryCount }}家 ></span>
</span> </span>
...@@ -173,8 +205,12 @@ ...@@ -173,8 +205,12 @@
</div> </div>
</div> </div>
<!-- 50%规则子企业弹框 --> <!-- 50%规则子企业弹框 -->
<RuleSubsidiaryDialog v-model="subsidiaryDialogVisible" :company-name="currentSubsidiaryCompanyName" <RuleSubsidiaryDialog
:total-count="currentSubsidiaryCount" :data-list="currentSubsidiaryList" /> v-model="subsidiaryDialogVisible"
:company-name="currentSubsidiaryCompanyName"
:total-count="currentSubsidiaryCount"
:data-list="currentSubsidiaryList"
/>
</div> </div>
</template> </template>
...@@ -267,7 +303,7 @@ const getSanctionOverviewList = async () => { ...@@ -267,7 +303,7 @@ const getSanctionOverviewList = async () => {
removeCount.value = data.removeCount || 0; removeCount.value = data.removeCount || 0;
removeRuleCount.value = data.removeRuleCount || 0; removeRuleCount.value = data.removeRuleCount || 0;
const list = data.sanList || []; const list = activeTab.value === "add" ? data.sanList : data.removeList || [];
sanctionList.value = list.map(item => ({ sanctionList.value = list.map(item => ({
reason: item.sanReason, reason: item.sanReason,
entities: (item.orgList || []).map(org => ({ entities: (item.orgList || []).map(org => ({
...@@ -281,8 +317,8 @@ const getSanctionOverviewList = async () => { ...@@ -281,8 +317,8 @@ const getSanctionOverviewList = async () => {
subsidiaryText: subsidiaryText:
org.ruleOrgList && org.ruleOrgList.length > 0 org.ruleOrgList && org.ruleOrgList.length > 0
? (org.ruleOrgList[0].orgName.length > 10 ? (org.ruleOrgList[0].orgName.length > 10
? org.ruleOrgList[0].orgName.slice(0, 10) + "..." ? org.ruleOrgList[0].orgName.slice(0, 10) + "..."
: org.ruleOrgList[0].orgName) + "...等" : org.ruleOrgList[0].orgName) + "...等"
: "" : ""
})) }))
})); }));
...@@ -342,10 +378,12 @@ const getUrlParams = () => { ...@@ -342,10 +378,12 @@ const getUrlParams = () => {
// 单次制裁-制裁概况-制裁实体国家分布 // 单次制裁-制裁概况-制裁实体国家分布
const getEntityCountry = async () => { const getEntityCountry = async () => {
try { try {
const res = await getSingleSanctionEntityCountry({ const params = {
sanRecordId: sanRecordId.value, sanRecordId: sanRecordId.value,
sanTypeId: sanTypeId.value || 1 // 实体清单固定1 sanTypeId: sanTypeId.value || 1 // 默认为1
}); };
console.log("params --:", params);
const res = await getSingleSanctionEntityCountry(params);
if (res.code === 200) { if (res.code === 200) {
const rawData = res.data || []; const rawData = res.data || [];
const maxCount = Math.max(...rawData.map(item => item.count || 0), 1); const maxCount = Math.max(...rawData.map(item => item.count || 0), 1);
...@@ -413,7 +451,8 @@ const formattedData = computed(() => { ...@@ -413,7 +451,8 @@ const formattedData = computed(() => {
fileCode: info.fileCode ? `${info.fileCode} ` : "", fileCode: info.fileCode ? `${info.fileCode} ` : "",
administrativeOrderId: info.administrativeOrderId ? `No. ${info.administrativeOrderId}` : "", administrativeOrderId: info.administrativeOrderId ? `No. ${info.administrativeOrderId}` : "",
postPersonName: info.postPersonName, postPersonName: info.postPersonName,
domains: info.domainNames domains: info.domainNames,
avartar: info.postPersonAvatarUrl
}; };
}); });
...@@ -510,6 +549,7 @@ const entityDistribution = ref([ ...@@ -510,6 +549,7 @@ const entityDistribution = ref([
const sanTypeId = ref(""); const sanTypeId = ref("");
onMounted(() => { onMounted(() => {
// 获取路由参数中的sanTypeId // 获取路由参数中的sanTypeId
console.log("route.query.sanTypeId --:", route.query.sanTypeId);
sanTypeId.value = route.query.sanTypeId || "1"; sanTypeId.value = route.query.sanTypeId || "1";
// 获取URL参数 // 获取URL参数
getUrlParams(); getUrlParams();
......
...@@ -239,8 +239,6 @@ const handlePageChange = async newPage => { ...@@ -239,8 +239,6 @@ const handlePageChange = async newPage => {
// ========== 选择某项 ========== // ========== 选择某项 ==========
const selectSanction = item => { const selectSanction = item => {
selectedSanctionId.value = item.id; selectedSanctionId.value = item.id;
// 可在此处触发其他逻辑,如查看详情
// 替换当前路由参数,并重新加载数据
router.replace({ router.replace({
path: window.location.pathname, path: window.location.pathname,
query: { query: {
...@@ -248,11 +246,8 @@ const selectSanction = item => { ...@@ -248,11 +246,8 @@ const selectSanction = item => {
sanTypeId: item.sanTypeId sanTypeId: item.sanTypeId
} }
}); });
// 3. 立即重新加载数据(关键!)
getSingleSanctionOverviewData();
// 4. 可选:关闭弹窗
sanctionModalVisible.value = false; sanctionModalVisible.value = false;
window.location.reload();
}; };
// ========== 关闭弹窗时重置 ========== // ========== 关闭弹窗时重置 ==========
...@@ -278,6 +273,7 @@ onMounted(() => { ...@@ -278,6 +273,7 @@ onMounted(() => {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
padding-bottom: 50px;
.header { .header {
width: 100%; width: 100%;
height: 148px; height: 148px;
......
<template>
<div class="wrap">
<div class="scroll-inner">
<div class="header">
<div class="header-top">
<div class="header-top-left">
<img src="../assets/images/box1-logo.png" alt="" />
<div>
<div class="title">{{ thinkInfo.name }}</div>
<div class="en-title">
{{ thinkInfo.ename }}.{{ thinkInfo.times }}
</div>
<div class="tag-box">
<!-- <div class="tag-box" v-for="value,index in thinkInfo.tags" :key="index">
<div class="tag">{{ value.industryName }}</div>
</div> -->
<AreaTag v-for="(value, index) in thinkInfo.tags" :key="index" :tagName="value.industryName"></AreaTag>
</div>
</div>
</div>
<div class="header-top-right">
<div class="image-name-box">
<div class="image"><img :src="thinkInfo.thinkTankLogoUrl" alt="" /></div>
<div class="name">{{ thinkInfo.thinkTankName }}</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" @click="goToOfficialWebsite()">{{ "查看官网" }}</div>
</div> -->
<div class="btn">
<div class="icon">
<img src="./images/pdf-image.png" alt="" />
</div>
<div class="text" @click="toReport()">{{ "文档下载" }}</div>
</div>
<!-- <div class="btn" @click="handleDownloadDocument">
<div class="icon">
<img src="./images/btn-icon3.png" alt="" />
</div>
<div class="text">{{ "文档下载" }}</div>
</div> -->
<div class="btn btn1" @click="handleAnalysisClick">
<div class="icon">
<img src="./images/paper-image.png" alt="" />
</div>
<div class="text">{{ "查看原文" }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="bottom-row">
<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">
{{ box1Data }}
</div> -->
<AnalysisBox title="基本信息" :showAllBtn="true">
<div class="box1-main">
<div class="text-box">
<div class="time">
<div class="time-title">发布时间:</div>
<div class="time-content">{{ publishTime }}</div>
</div>
<div class="topic">
<div class="topic-title">报告主题:</div>
<div class="topic-content">{{ reportTopic }}</div>
</div>
<div class="author">
<div class="author-title">报告作者:</div>
<div class="author-content">
<template v-if="Array.isArray(reportAuthors) && reportAuthors.length">
<span v-if="reportAuthors.length === 1">
{{ reportAuthors[0].name }}
</span>
<!-- 多个作者:显示第一个 + 等 -->
<span v-else>
{{ reportAuthors[0].name }}{{ reportAuthors.length }}
</span>
</template>
</div>
</div>
</div>
<div class="author-box">
<div class="author-item" v-for="(author, idx) in reportAuthors" :key="idx"
v-if="Array.isArray(reportAuthors) && reportAuthors.length"
@click="handleClickReportAuthor(author)">
<div class="image"><img :src="author.avatar ? author.avatar : DefaultIcon1" alt=""
@error="() => { if (author.avatar) author.avatar = null; }" /></div>
<div class="author-text">
<div class="author-name">{{ author.name }}</div>
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
<div class="box5">
<AnalysisBox title="关键词云" :showAllBtn="true">
<div class="box5-main">
<template v-if="!hasBox5ChartData">
<el-empty class="box5-el-empty" description="暂无数据" :image-size="100" />
</template>
<template v-else>
<div class="box5Chart">
<!-- 有数据后再挂载子组件:子组件仅在 onMounted 初始化,异步数据到达后需 v-if + key 强制重新挂载 -->
<WordCloudChart v-if="box5Data.length" :key="box5WordCloudKey" :data="box5Data" width="432px"
height="272px" />
</div>
<div class="box5-footer">
<TipTab :text="REPORT_ANALYSIS_TIP_BOX5" />
</div>
<div class="ai-wrap" @mouseenter="handleSwitchAiContentShowBox5(true)">
<AiButton />
</div>
<div class="ai-content" v-if="isShowAiContentBox5" @mouseleave="handleSwitchAiContentShowBox5(false)">
<AiPane :aiContent="aiContentBox5" />
</div>
</template>
</div>
</AnalysisBox>
</div>
</div>
<div class="right">
<div class="box3">
<AnalysisBox title="内容摘要" :showAllBtn="true">
<div class="box3-main">
<AiSummary>
<template #summary-content>
{{ box1Data }}
</template>
</AiSummary>
</div>
</AnalysisBox>
</div>
<div class="box4">
<AnalysisBox title="听证会内容" :showAllBtn="true">
<div class="search-box">
<el-input placeholder="搜索内容" v-model="searchOpinions" style="width: 180px"
@keyup.enter="handleSearchOpinions" />
<div class="icon">
<img src="../assets/images/Line_Search.png" alt="" @click="handleSearchOpinions" />
</div>
</div>
<div class="box4-main">
<div class="box4-main-main">
<div class="box4-item" v-for="(item, index) in filteredOpinions"
:key="item.id != null ? item.id : index">
<div class="top-row">
<div class="left">
{{ index + 1 }}
</div>
<div class="center">
<div class="title" v-html="highlightOpinionText(item.titleZh)"></div>
<div>
<img src="./images/image-open.png" alt="" class="center-image"
@click="handleOpenReportOriginal(item)" />
</div>
<div>
<img v-if="!isOpinionExpanded(item, index)" src="./images/image-down.png" alt=""
class="center-image" @click="toggleOpinion(item, index)" />
<img v-else src="./images/image-up.png" alt="" class="center-image"
@click="toggleOpinion(item, index)" />
</div>
</div>
</div>
<div v-if="isOpinionExpanded(item, index)" class="desc"
v-html="highlightOpinionText(item.contentZh)">
</div>
<!-- <div class="right"> -->
<!-- <div class="tag" v-for="(val, idx) in item.hylyList" :key="idx">
{{ val }}
</div>
<div class="tag" v-for="(val, idx) in item.serialNum" :key="idx">
{{ val }}
</div> -->
<!-- <AreaTag v-for="(val, idx) in item.hylyList" :key="idx" :tagName="val"></AreaTag>
</div> -->
<!-- <div class="more">
<img src="@/assets/icons/open.png" alt="" />
</div> -->
</div>
</div>
<div class="box4-main-footer">
<div class="info">共{{ opinionsTotal }}条听证会提问</div>
<div class="page-box">
<el-pagination :page-size="pageSize" background layout="prev, pager, next" :total="opinionsTotal"
@current-change="handleCurrentChange" :current-page="currentPage" />
</div>
</div>
</div>
</AnalysisBox>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import DefaultIcon1 from '@/assets/icons/default-icon1.png'
import WarningPane from "@/components/base/WarningPane/index.vue"
import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import SearchContainer from "@/components/SearchContainer.vue";
import { ref, onMounted, computed, defineProps } from "vue";
import { ElMessage } from "element-plus";
import {
getThinkTankReportAbstract,
getThinkTankReportContent,
getThinkTankReportIndustry,
getThinkTankReportIndustryCloud,
getThinkTankReportViewpoint
} from "@/api/thinkTank/overview";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import { useRouter } from "vue-router";
import "echarts-wordcloud";
import AiSummary from '@/components/base/Ai/AiSummary/index.vue'
import { getPersonSummaryInfo } from "@/api/common/index";
import AiButton from "@/components/base/Ai/AiButton/index.vue";
import AiPane from "@/components/base/Ai/AiPane/index.vue";
import TipTab from "@/views/thinkTank/TipTab/index.vue";
const router = useRouter();
const goToAllThinkTank = () => {
const thinkTankId = props?.thinkInfo?.thinkTankId || props?.thinkInfo?.id;
const route = router.resolve({
name: "MultiThinkTankViewAnalysis",
params: { id: thinkTankId }
});
window.open(route.href, "_blank");
};
const props = defineProps({
reportList: {
type: Object,
default: () => ({})
}
});
const thinkInfo = ref({
name: "探讨中国开发和管理的跨大陆电网的安全影响",
ename: "调查项目",
tags: [{ industryName: "深海" }, { industryName: "人工智能" }],
thinkTankName: "兰德科技智库",
thinkTankLogoUrl: "http://8.140.26.4:10010/kjb-files/images/org/land.webp",
times: "2024-05-28"
})
const REPORT_ANALYSIS_TIP_BOX5 =
"智库报告关键词云,数据来源:美国兰德公司官网";
// 刷新后默认展示「报告关键词云」AI 总结
const isShowAiContentBox5 = ref(true);
const aiContentBox5 = ref("");
const isBox5InterpretLoading = ref(false);
const handleSwitchAiContentShowBox5 = (val) => {
isShowAiContentBox5.value = val;
if (val) {
fetchBox5ChartInterpretation();
}
};
const searchOpinions = ref('');
const escapeHtml = (text) => {
return String(text ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
};
const escapeRegExp = (text) => {
return String(text ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
const highlightOpinionText = (text) => {
const safeText = escapeHtml(text);
const keyword = (searchOpinions.value || "").trim();
if (!keyword) return safeText;
const pattern = new RegExp(`(${escapeRegExp(keyword)})`, "gi");
return safeText.replace(pattern, `<span class="opinion-keyword-highlight">$1</span>`);
};
const handleSearchOpinions = () => {
currentPage.value = 1;
handleGetThinkTankReportViewpoint();
};
/** 可同时展开多条;用 id 区分项,避免翻页后索引与展开状态错位 */
const expandedOpinionKeys = ref(new Set());
const filteredOpinions = computed(() => majorOpinions.value);
const opinionsTotal = computed(() => total.value);
const getOpinionExpandKey = (item, index) => {
if (item != null && item.id != null && item.id !== "") {
return String(item.id);
}
return `idx-${index}`;
};
const isOpinionExpanded = (item, index) => {
return expandedOpinionKeys.value.has(getOpinionExpandKey(item, index));
};
const toggleOpinion = (item, index) => {
const key = getOpinionExpandKey(item, index);
const next = new Set(expandedOpinionKeys.value);
if (next.has(key)) {
next.delete(key);
} else {
next.add(key);
}
expandedOpinionKeys.value = next;
};
const publishTime = computed(() => {
const info = props.thinkInfo || {};
// 优先用 times,其次用 reportTime 的日期部分
if (info.times) return info.times;
if (info.reportTime && typeof info.reportTime === "string") {
return info.reportTime.split("T")[0];
}
return "";
});
const reportTopic = computed(() => {
const info = props.thinkInfo || {};
return info.summary;
});
const reportAuthors = computed(() => {
const info = props.thinkInfo || {};
if (Array.isArray(info.authors) && info.authors.length) {
return info.authors;
}
return [];
});
// 点击报告作者头像,跳转到人物主页
// 与核心研究人员逻辑一致:核心依赖 personId,本页面依赖作者的 id(作为 personId 传入)
const handleClickReportAuthor = async (author) => {
const personId = author?.id;
if (!personId) return;
const params = { personId };
const res = await getPersonSummaryInfo(params);
if (res.code !== 200 || !res.data) return;
window.sessionStorage.setItem("curTabName", author?.name || "");
const route = router.resolve({
path: "/characterPage",
query: {
personId
}
});
window.open(route.href, "_blank");
};
const riskSignal = computed(() => {
const info = props.thinkInfo || {};
return info.riskSignal;
});
// 内容摘要
const box1Data =
ref(`包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。
为了应对这一挑战,兰德大学的研究人员对美中竞争进行了经济和制度分析,进行了参与式的远见练习,以了解确保美国经济健康的长期路径,并创建了两个经济竞争游戏,探索多个国家在相互交流的同时确保经济健康的动态...`);
//获取内容摘要
const handleGetThinkTankReportAbstract = async () => {
try {
const res = await getThinkTankReportAbstract(router.currentRoute._value.params.id);
console.log("内容摘要", res);
if (res.code === 200 && res.data) {
box1Data.value = res.data;
}
} catch (error) {
console.error("获取内容摘要error", error);
}
};
// 涉及科技领域
const areaList = ref([]);
const activeArea = ref(6);
const handleClickArea = area => {
activeArea.value = area;
handleGetThinkTankReportIndustryCloud();
};
const box2Data = ref([
// {
// name: "通用人工智能",
// value: 100
// },
// {
// name: "AI芯片",
// value: 66
// },
// {
// name: "计算能力又是",
// value: 72
// },
// {
// name: "基准测试",
// value: 88
// },
// {
// name: "出口管制",
// value: 78
// },
// {
// name: "军事AI",
// value: 85
// },
// {
// name: "生态系统",
// value: 88
// },
// {
// name: "模型能力",
// value: 89
// }
]);
// 报告关键词云
const box5Data = ref([]);
const hasBox5ChartData = computed(() => Array.isArray(box5Data.value) && box5Data.value.length > 0);
/** 词云子组件不 watch 数据,每次接口成功有数据时递增 key,强制重新挂载以触发 onMounted */
const box5WordCloudKey = ref(0);
//获取科技领域词云
const handleGetThinkTankReportIndustryCloud = async () => {
try {
const params = {
id: router.currentRoute._value.params.id
// industryId: activeArea.value
};
const res = await getThinkTankReportIndustryCloud(params);
console.log("科技领域词云", res);
if (res.code === 200 && res.data) {
const data = (res.data || []).map(item => ({
name: item.clause,
value: item.count
}));
// 该接口数据用于「报告关键词云」
box5Data.value = data;
if (data.length) {
box5WordCloudKey.value += 1;
}
// 刷新后默认展开 AI:数据就绪即触发解读
if (isShowAiContentBox5.value) {
fetchBox5ChartInterpretation();
}
} else {
box5Data.value = [];
}
} catch (error) {
console.error("获取科技领域词云error", error);
box5Data.value = [];
}
};
//涉及科技领域
const handleGetThinkTankReportIndustry = async () => {
try {
const res = await getThinkTankReportIndustry(router.currentRoute._value.params.id);
console.log("涉及科技领域", res);
if (res.code === 200 && res.data) {
areaList.value = res.data;
}
} catch (error) {
console.error("获取涉及科技领域error", error);
}
};
// 主要观点
const majorOpinions = ref([
{
id: 1,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 2,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "私有经济",
status: 2
}
]
}
]);
//处理点击详情页事件
const handleOpenReportOriginal = item => {
const route = router.resolve({
name: "ReportOriginal",
params: {
id: router.currentRoute._value.params.id
},
query: {
currentPage: currentPage.value,
pageSize: pageSize.value,
opinionId: item?.id ?? "",
opinionContent: item?.content ?? ""
}
});
window.open(route.href, "_blank");
};
const tabActiveName = ref("报告分析");
const switchTab = name => {
tabActiveName.value = name;
};
// 处理页码改变事件
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const handleCurrentChange = page => {
currentPage.value = page;
handleGetThinkTankReportViewpoint();
};
// 获取报告核心论点(支持搜索)
const handleGetThinkTankReportViewpoint = async () => {
try {
const params = {
reportId: router.currentRoute._value.params.id,
currentPage: currentPage.value - 1,
pageSize: pageSize.value,
keyword: (searchOpinions.value || "").trim(),
orgIds: ""
};
const res = await getThinkTankReportViewpoint(params);
console.log("核心论点", res.data);
if (res.code === 200 && res.data) {
const nextOpinions = res.data.content || [];
majorOpinions.value = nextOpinions;
total.value = res.data.totalElements || 0;
// 默认:第一条展开,其余关闭
const nextExpandedKeys = new Set();
if (Array.isArray(nextOpinions) && nextOpinions.length > 0) {
nextExpandedKeys.add(getOpinionExpandKey(nextOpinions[0], 0));
}
expandedOpinionKeys.value = nextExpandedKeys;
}
} catch (error) {
console.error("获取主要观点error", error);
}
};
// 获取图表分析内容
const box3AnalysisContent = ref("");
const handleGetBox3AnalysisContent = async textJson => {
const params = {
text: textJson
};
const res = await getChartAnalysis(params);
console.log("图表解析内容", res);
};
const getInterpretationTextFromChartResponse = (res) => {
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
return (
first?.["解读"] ||
first?.["interpretation"] ||
first?.["analysis"] ||
first?.["content"] ||
""
);
};
const appendAiInterpretationChunk = (targetRef, chunk, loadingText = "解读生成中…") => {
if (!chunk) {
return;
}
const current = String(targetRef.value || "");
const base = current === loadingText ? "" : current;
targetRef.value = base + String(chunk);
};
const fetchBox5ChartInterpretation = async () => {
const list = Array.isArray(box5Data.value) ? box5Data.value : [];
if (!list.length) {
aiContentBox5.value = "暂无图表数据";
return;
}
const hasValidContent =
aiContentBox5.value &&
aiContentBox5.value !== "解读生成中…" &&
aiContentBox5.value !== "解读加载失败" &&
aiContentBox5.value !== "暂无图表数据";
if (hasValidContent || isBox5InterpretLoading.value) {
return;
}
isBox5InterpretLoading.value = true;
aiContentBox5.value = "解读生成中…";
const chartPayload = {
type: "词云图",
name: "报告关键词云",
data: list.map((item) => ({
name: item.name,
value: item.value
}))
};
try {
const res = await getChartAnalysis(
{ text: JSON.stringify(chartPayload) },
{
onChunk: chunk => {
appendAiInterpretationChunk(aiContentBox5, chunk);
}
}
);
const text = getInterpretationTextFromChartResponse(res);
aiContentBox5.value = text || aiContentBox5.value || "未返回有效解读内容";
} catch (error) {
console.error("报告关键词云图表解读请求失败", error);
aiContentBox5.value = "解读加载失败";
} finally {
isBox5InterpretLoading.value = false;
}
};
onMounted(() => {
handleGetThinkTankReportAbstract();
handleGetThinkTankReportViewpoint();
handleGetThinkTankReportIndustry();
handleGetThinkTankReportIndustryCloud();
});
</script>
<style lang="scss" scoped>
.wrap {
position: absolute;
inset: 0;
box-sizing: border-box;
width: 100%;
overflow: hidden;
.scroll-inner {
box-sizing: border-box;
width: 100%;
height: 100%;
min-height: 0;
padding-bottom: 16px;
overflow-x: hidden;
overflow-y: auto;
scrollbar-gutter: stable;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
}
}
.header {
width: 100%;
height: 126px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
position: sticky;
top: 0;
z-index: 99999;
overflow: hidden;
.header-top {
margin: 0 auto;
margin-top: 20px;
width: 1600px;
display: flex;
justify-content: space-between;
.header-top-left {
display: flex;
img {
width: 72px;
height: 88px;
}
.title {
margin-left: 16px;
height: 26px;
color: rgb(59, 65, 75);
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 20px;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.en-title {
margin-top: 4px;
margin-left: 16px;
height: 24px;
color: rgb(95, 101, 108);
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
.tag-box {
margin-top: 12px;
display: flex;
gap: 8px;
margin-left: 16px;
}
}
.header-top-right {
display: flex;
flex-direction: column;
text-align: right;
align-items: flex-end;
.image-name-box {
width: 118px;
height: 24px;
gap: 6px;
text-align: right;
display: flex;
justify-content: flex-end;
.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;
}
.image {
width: 16px;
height: 16px;
margin-top: 5px;
img {
width: 100%;
height: 100%;
}
}
}
.btn-box {
display: flex;
gap: 12px;
margin-top: 34px;
.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;
justify-content: center;
gap: 8px;
.icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 66px;
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;
}
}
.btn1 {
border-radius: 6px;
background: var(--color-main-active);
.text {
color: rgba(255, 255, 255, 1);
}
}
}
.time {
height: 24px;
margin-top: 5px;
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;
}
}
}
}
.bottom-row {
flex-direction: row;
justify-content: center;
display: flex;
gap: 16px;
.left {
gap: 16px;
display: flex;
flex-direction: column;
margin-top: 16px;
.box1 {
width: 480px;
// 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 {
width: 480px;
.text-box {
width: 437px;
margin-left: 22px;
margin-top: 8px;
gap: 12px;
display: flex;
flex-direction: column;
.time {
height: 24px;
display: flex;
gap: 4px;
.time-title {
width: 88px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
color: rgb(59, 65, 75);
}
.time-content {
width: 345px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
color: rgb(59, 65, 75);
}
}
.topic {
display: flex;
gap: 4px;
.topic-title {
width: 88px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
color: rgb(59, 65, 75);
}
.topic-content {
width: 345px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
color: rgb(59, 65, 75);
}
}
.author {
display: flex;
gap: 4px;
.author-title {
width: 88px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 1px;
text-align: left;
color: rgb(59, 65, 75);
}
.author-content {
width: 345px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
color: rgb(59, 65, 75);
}
}
}
.author-box {
width: 437px;
height: auto;
margin-top: 34px;
margin-left: 18px;
display: grid;
grid-template-columns: 1fr 1fr;
/* 两列等宽 */
column-gap: 4px;
/* 左右间距(同一行) */
row-gap: 8px;
/* 上下间距(同一列) */
margin-bottom: 38px;
.author-item {
width: 213px;
height: 49px;
display: flex;
gap: 11px;
.image {
width: 42px;
height: 42px;
margin-top: 3px;
margin-left: 3px;
display: inline-block;
cursor: pointer;
img {
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.author-text {
width: 154px;
height: 49px;
.author-name {
width: 154px;
height: 24px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 0;
text-align: left;
color: rgb(59, 65, 75);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.author-position {
width: 154px;
height: 22px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 14px;
line-height: 22px;
letter-spacing: 0;
text-align: left;
color: rgb(95, 101, 108);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
}
.box5 {
width: 480px;
height: 415px;
.box5-main {
width: 480px;
height: 361px;
padding: 24px 24px 65px 24px;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
position: relative;
.box5Chart {
width: 100%;
height: 100%;
margin: 0 auto;
overflow: hidden;
}
.box5-footer {
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
bottom: 20px;
left: 32px;
}
.ai-wrap {
position: absolute;
bottom: 18px;
right: 0;
cursor: pointer;
}
.ai-content {
position: absolute;
bottom: 0;
right: 0;
min-width: 480px;
min-height: 156px;
}
}
}
.box2 {
width: 480px;
// 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: 436px;
margin-top: 5px;
margin-left: 23px;
.box2-item {
height: 103px;
width: 100%;
border-bottom: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1);
.box2-item-content {
width: 100%;
height: 90px;
margin-top: 7px;
display: flex;
.left {
width: 56px;
height: 74px;
margin-top: 8px;
img {
width: 100%;
height: 100%;
}
}
.right-content {
margin-left: 13px;
width: 365px;
height: 76px;
margin-top: 7px;
.report-title {
height: 48px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 0;
text-align: left;
/* 👇 下面是 两行文本超出省略 核心代码 */
display: -webkit-box;
-webkit-line-clamp: 2;
/* 限制显示 2 行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.report-footer {
margin-top: 4px;
height: 22px;
justify-content: space-between;
display: flex;
.report-time {
height: 22px;
width: 97px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 14px;
line-height: 22px;
letter-spacing: 0;
text-align: left;
color: rgb(95, 101, 108);
}
.report-footer-right {
height: 22px;
display: flex;
gap: 6px;
.footer-image {
width: 16px;
height: 16px;
margin-top: 3px;
img {
width: 100%;
height: 100%;
}
}
.think-name {
height: 22px;
}
}
}
}
}
}
}
.box2-btn {
margin-top: 16px;
margin-bottom: 21px;
margin-left: 23px;
width: 436px;
height: 36px;
background-color: rgb(5, 95, 194);
border-radius: 6px;
display: flex;
.btn-text {
color: rgb(255, 255, 255);
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 22px;
letter-spacing: 0;
margin-left: 120px;
margin-top: 7px;
}
.btn-image {
width: 13px;
height: 8px;
margin-left: 8px;
display: inline-block;
margin-top: 14px;
img {
width: 100%;
height: 100%;
display: block;
}
}
}
}
}
.right {
margin-top: 16px;
gap: 16px;
display: flex;
flex-direction: column;
.box3 {
width: 1103px;
// 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);
.box3-main {
width: 1058px;
margin-top: 3px;
margin-left: 22px;
padding-bottom: 22px;
:deep(.summary-main) {
margin-bottom: 25px;
font-family: "Source Han Sans CN";
font-weight: 400;
/* Regular 常规 */
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
color: rgb(59, 65, 75);
/* 两端对齐 */
}
.box3-top {
width: 1058px;
height: 48px;
background: linear-gradient(rgb(137, 193, 255, 0.1), rgb(255, 255, 255));
display: flex;
.top-title {
width: 1010px;
height: 32px;
margin-left: 24px;
margin-top: 16px;
.title-image {
width: 199px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
}
}
.box3-text {
width: 1006px;
margin-top: 24px;
margin-left: 26px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
text-justify: inter-ideograph;
}
}
}
.box4 {
width: 1103px;
height: auto;
// 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;
.search-box {
display: flex;
width: 180px;
height: 32px;
box-sizing: border-box;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 4px;
background: rgba(255, 255, 255, 1);
position: relative;
margin-top: 3px;
margin-bottom: 16px;
margin-left: 23px;
.icon {
width: 16px;
height: 16px;
cursor: pointer;
position: absolute;
right: 8px;
top: 8px;
display: flex;
justify-content: flex-end;
z-index: 10000;
img {
width: 100%;
height: 100%;
}
}
}
.box4-main {
width: 1057px;
height: auto;
margin: 0 auto;
.box4-main-main {
height: auto;
overflow: visible;
.box4-item {
width: 1057px;
box-sizing: border-box;
border-radius: 4px;
display: flex;
flex-direction: column;
position: relative;
border-bottom: 1px solid rgba(234, 236, 238, 1);
&:first-child {
border-top: 1px solid rgba(234, 236, 238, 1);
}
.top-row {
display: flex;
align-items: flex-start;
}
.left {
margin-top: 19px;
margin-left: 15px;
width: 24px;
height: 24px;
border-radius: 12px;
line-height: 24px;
text-align: center;
background: rgba(231, 243, 255, 1);
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 12px;
font-weight: 400;
letter-spacing: 0px;
}
.center {
min-height: 62px;
margin-left: 18px;
display: flex;
align-items: center;
// overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
.title {
width: 918px;
// height: 55px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
// text-overflow: ellipsis;
// white-space: nowrap;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.center-image {
width: 16px;
height: 24px;
margin-top: 12px;
margin-left: 18px;
}
}
.right {
margin-top: 26px;
width: 180px;
height: 22px;
display: flex;
margin-top: 26px;
margin-left: 20px;
height: 22px;
display: flex;
gap: 4px;
.tag {
height: 22px;
padding: 0 8px;
box-sizing: border-box;
border: 1px solid rgba(217, 247, 190, 1);
border-radius: 4px;
background: rgba(246, 255, 237, 1);
color: rgba(82, 196, 26, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 20px;
}
}
.more {
width: 16px;
height: 16px;
position: absolute;
top: 28px;
right: 20px;
img {
width: 100%;
height: 100%;
}
}
.desc {
padding-top: 22px;
padding-bottom: 23px;
padding-left: 56px; // 24(left) + 13(center margin) + 一点间距
color: rgb(59, 65, 75);
font-family: "Source Han Sans CN";
font-weight: 400;
/* Regular 常规 */
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
/* 两端对齐 */
border-top: 1px solid rgba(234, 236, 238, 1);
}
.title :deep(.opinion-keyword-highlight),
.desc :deep(.opinion-keyword-highlight) {
background-color: #fff59d;
}
}
}
.box4-main-footer {
height: 80px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 30px 5px;
box-sizing: border-box;
overflow: hidden;
.info {
flex: 1 1 auto;
min-width: 0;
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;
}
.page-box {
/* 最大 300px:允许变小,但绝不变大 */
flex: 0 1 300px;
width: 100%;
max-width: 300px;
min-width: 0;
display: flex;
justify-content: flex-end;
overflow: hidden;
}
.page-box :deep(.el-pagination) {
max-width: 100%;
min-width: 0;
overflow: hidden;
}
}
}
.box4-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%;
}
}
}
}
.box5 {
width: 1103px;
height: auto;
// 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;
}
}
}
}
:deep(.analysis-box-wrapper .wrapper-header) {
height: 54px !important;
display: flex;
align-items: center;
.header-title>div {
line-height: 54px;
}
}
</style>
...@@ -474,7 +474,7 @@ const handleOpenReportOriginalFromSource = (sv) => { ...@@ -474,7 +474,7 @@ const handleOpenReportOriginalFromSource = (sv) => {
const id = String(sv?.report_id ?? "").trim() const id = String(sv?.report_id ?? "").trim()
if (!id) return if (!id) return
const route = router.resolve({ const route = router.resolve({
name: "ReportOriginal", name: "ReportDetail",
params: { id } params: { id }
}) })
window.open(route.href, "_blank") window.open(route.href, "_blank")
......
<template>
<div class="wrap">
<div class="scroll-inner">
<div class="header">
<div class="header-top">
<div class="header-top-left">
<img src="../assets/images/box1-logo.png" alt="" />
<div>
<div class="title">{{ thinkInfo.name }}</div>
<div class="en-title">
{{ thinkInfo.ename }}
</div>
</div>
</div>
<div class="header-top-right">
<div class="image-name-box">
<div class="image"><img src="../assets/images/box1-logo.png" alt="" /></div>
<div class="name">{{ thinkInfo.thinkTankName }}</div>
</div>
<div class="tag-box">
<AreaTag v-for="(value, index) in thinkInfo.tags" :key="index" :tagName="value.industryName"></AreaTag>
</div>
</div>
</div>
</div>
<div class="bottom-row">
<div class="left">
<div class="box1">
<AnalysisBox title="关键词云" :showAllBtn="true">
<div class="box1-main">
<template v-if="!hasBox5ChartData">
<el-empty class="box5-el-empty" description="暂无数据" :image-size="100" />
</template>
<template v-else>
<div class="box5Chart">
<!-- 有数据后再挂载子组件:子组件仅在 onMounted 初始化,异步数据到达后需 v-if + key 强制重新挂载 -->
<WordCloudChart v-if="box5Data.length" :key="box5WordCloudKey" :data="box5Data" width="432px"
height="272px" />
</div>
<div class="box1-footer">
<TipTab :text="REPORT_ANALYSIS_TIP_BOX5" />
</div>
<div class="ai-wrap" @mouseenter="handleSwitchAiContentShowBox5(true)">
<AiButton />
</div>
<div class="ai-content" v-if="isShowAiContentBox5" @mouseleave="handleSwitchAiContentShowBox5(false)">
<AiPane :aiContent="aiContentBox5" />
</div>
</template>
</div>
</AnalysisBox>
</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-content" id="box2Chart"></div>
</div> -->
<AnalysisBox title="项目报告" :showAllBtn="true">
<div class="box2-main">
<div class="box2-item" v-for="(report, idx) in reportList" :key="idx">
<div class="box2-item-content">
<div class="left"><img :src="report.image" alt="" /></div>
<div class="right-content">
<div class="report-title">{{ report.name }}</div>
<div class="report-footer">
<div class="report-time">{{ report.postDate }}</div>
<div class="report-footer-right">
<div class="footer-image">
<img :src="report.thinktankLogo" alt="" />
</div>
<div class="think-name">{{ report.thinktankName }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
</div>
<div class="right">
<div class="box3">
<AnalysisBox title="内容摘要" :showAllBtn="true">
<div class="box3-main">
<AiSummary>
<template #summary-content>
{{ box1Data }}
</template>
</AiSummary>
</div>
</AnalysisBox>
</div>
<div class="box4">
<AnalysisBox title="项目背景" :showAllBtn="true">
<div class="box4-main">
<div class="text">
{{
"可再生能源和清洁能源创新为摆脱对化石燃料的依赖提供了机会。然而,可再生能源的间歇性也对电网的实时供需平衡构成挑战。作为解决方案,中国提出开发全球能源互联(GEI)倡议,通过超高压输电线路和智能技术,直接将可再生能源生产者与全球消费者连接起来。北京认识到GEI的潜力,正站在GEI发展的前沿,领导关键推动技术的研究,输出中国技术和标准,支持海外发电和输电基础设施的发展。中国还在联合国和海湾合作委员会等国际组织推动GEI"
}}
</div>
</div>
</AnalysisBox>
</div>
<div class="box5">
<AnalysisBox title="项目团队" :showAllBtn="true">
<div class="box5-main">
<div class="box5-main-item-box">
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
<div class="item">
<div class="item-left">
<img src="../assets/images/rand-image.png" alt="" />
</div>
<div class="item-right">
<div class="item-name">{{ "纳迪娅·阿尔马萨尔基" }} </div>
<div class="item-position">{{ "副秘书" }}</div>
</div>
</div>
</div>
</div>
</AnalysisBox>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import DefaultIcon1 from '@/assets/icons/default-icon1.png'
import WarningPane from "@/components/base/WarningPane/index.vue"
import WordCloudChart from "@/components/base/WordCloundChart/index.vue"
import SearchContainer from "@/components/SearchContainer.vue";
import { ref, onMounted, computed, defineProps } from "vue";
import { ElMessage } from "element-plus";
import {
getThinkTankReportAbstract,
getThinkTankReportContent,
getThinkTankReportIndustry,
getThinkTankReportIndustryCloud,
getThinkTankReportViewpoint
} from "@/api/thinkTank/overview";
import { getChartAnalysis } from "@/api/aiAnalysis/index";
import { useRouter } from "vue-router";
import "echarts-wordcloud";
import AiSummary from '@/components/base/Ai/AiSummary/index.vue'
import { getPersonSummaryInfo } from "@/api/common/index";
import AiButton from "@/components/base/Ai/AiButton/index.vue";
import AiPane from "@/components/base/Ai/AiPane/index.vue";
import TipTab from "@/views/thinkTank/TipTab/index.vue";
const router = useRouter();
const thinkInfo = ref({
name: "探讨中国开发和管理的跨大陆电网的安全影响",
ename: "调查项目",
tags: [{ industryName: "深海" }, { industryName: "人工智能" }],
thinkTankName: "兰德科技智库"
})
const REPORT_ANALYSIS_TIP_BOX5 =
"智库报告关键词云,数据来源:美国兰德公司官网";
// 刷新后默认展示「报告关键词云」AI 总结
const isShowAiContentBox5 = ref(true);
const aiContentBox5 = ref("");
const isBox5InterpretLoading = ref(false);
const handleSwitchAiContentShowBox5 = (val) => {
isShowAiContentBox5.value = val;
if (val) {
fetchBox5ChartInterpretation();
}
};
const searchOpinions = ref('');
const escapeHtml = (text) => {
return String(text ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
};
const escapeRegExp = (text) => {
return String(text ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};
/** 可同时展开多条;用 id 区分项,避免翻页后索引与展开状态错位 */
const expandedOpinionKeys = ref(new Set());
const getOpinionExpandKey = (item, index) => {
if (item != null && item.id != null && item.id !== "") {
return String(item.id);
}
return `idx-${index}`;
};
// 内容摘要
const box1Data =
ref(`包括经济竞争在内的美中竞争自2017年以来一直在定义美国外交政策。这两个经济体是世界上第一和第二大国家经济体,并且深深交织在一起。改变关系,无论多么必要,可能是昂贵的。因此,美国面临着一项挑战,确保其经济在耦合的战略竞争条件下满足国家的需求。
为了应对这一挑战,兰德大学的研究人员对美中竞争进行了经济和制度分析,进行了参与式的远见练习,以了解确保美国经济健康的长期路径,并创建了两个经济竞争游戏,探索多个国家在相互交流的同时确保经济健康的动态...`);
//获取内容摘要
const handleGetThinkTankReportAbstract = async () => {
try {
const res = await getThinkTankReportAbstract(router.currentRoute._value.params.id);
console.log("内容摘要", res);
if (res.code === 200 && res.data) {
box1Data.value = res.data;
}
} catch (error) {
console.error("获取内容摘要error", error);
}
};
// 涉及科技领域
const areaList = ref([]);
const activeArea = ref(6);
const handleClickArea = area => {
activeArea.value = area;
handleGetThinkTankReportIndustryCloud();
};
const box2Data = ref([
// {
// name: "通用人工智能",
// value: 100
// },
// {
// name: "AI芯片",
// value: 66
// },
// {
// name: "计算能力又是",
// value: 72
// },
// {
// name: "基准测试",
// value: 88
// },
// {
// name: "出口管制",
// value: 78
// },
// {
// name: "军事AI",
// value: 85
// },
// {
// name: "生态系统",
// value: 88
// },
// {
// name: "模型能力",
// value: 89
// }
]);
// 报告关键词云
const box5Data = ref([]);
const hasBox5ChartData = computed(() => Array.isArray(box5Data.value) && box5Data.value.length > 0);
/** 词云子组件不 watch 数据,每次接口成功有数据时递增 key,强制重新挂载以触发 onMounted */
const box5WordCloudKey = ref(0);
//获取科技领域词云
const handleGetThinkTankReportIndustryCloud = async () => {
try {
const params = {
id: router.currentRoute._value.params.id
// industryId: activeArea.value
};
const res = await getThinkTankReportIndustryCloud(params);
console.log("科技领域词云", res);
if (res.code === 200 && res.data) {
const data = (res.data || []).map(item => ({
name: item.clause,
value: item.count
}));
// 该接口数据用于「报告关键词云」
box5Data.value = data;
if (data.length) {
box5WordCloudKey.value += 1;
}
// 刷新后默认展开 AI:数据就绪即触发解读
if (isShowAiContentBox5.value) {
fetchBox5ChartInterpretation();
}
} else {
box5Data.value = [];
}
} catch (error) {
console.error("获取科技领域词云error", error);
box5Data.value = [];
}
};
//涉及科技领域
const handleGetThinkTankReportIndustry = async () => {
try {
const res = await getThinkTankReportIndustry(router.currentRoute._value.params.id);
console.log("涉及科技领域", res);
if (res.code === 200 && res.data) {
areaList.value = res.data;
}
} catch (error) {
console.error("获取涉及科技领域error", error);
}
};
// 主要观点
const majorOpinions = ref([
{
id: 1,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "关税",
status: 2
},
{
name: "跨境电商",
status: 1
}
]
},
{
id: 2,
title: "我是示例标题",
desc: "我是示例内容",
tagList: [
{
name: "私有经济",
status: 2
}
]
}
]);
//处理点击详情页事件
const handleOpenReportOriginal = item => {
const route = router.resolve({
name: "ReportOriginal",
params: {
id: router.currentRoute._value.params.id
},
query: {
currentPage: currentPage.value,
pageSize: pageSize.value,
opinionId: item?.id ?? "",
opinionContent: item?.content ?? ""
}
});
window.open(route.href, "_blank");
};
const tabActiveName = ref("报告分析");
const switchTab = name => {
tabActiveName.value = name;
};
// 处理页码改变事件
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const handleCurrentChange = page => {
currentPage.value = page;
handleGetThinkTankReportViewpoint();
};
// 获取报告核心论点(支持搜索)
const handleGetThinkTankReportViewpoint = async () => {
try {
const params = {
reportId: router.currentRoute._value.params.id,
currentPage: currentPage.value - 1,
pageSize: pageSize.value,
keyword: (searchOpinions.value || "").trim(),
orgIds: ""
};
const res = await getThinkTankReportViewpoint(params);
console.log("核心论点", res.data);
if (res.code === 200 && res.data) {
const nextOpinions = res.data.content || [];
majorOpinions.value = nextOpinions;
total.value = res.data.totalElements || 0;
// 默认:第一条展开,其余关闭
const nextExpandedKeys = new Set();
if (Array.isArray(nextOpinions) && nextOpinions.length > 0) {
nextExpandedKeys.add(getOpinionExpandKey(nextOpinions[0], 0));
}
expandedOpinionKeys.value = nextExpandedKeys;
}
} catch (error) {
console.error("获取主要观点error", error);
}
};
// 获取图表分析内容
const box3AnalysisContent = ref("");
const handleGetBox3AnalysisContent = async textJson => {
const params = {
text: textJson
};
const res = await getChartAnalysis(params);
console.log("图表解析内容", res);
};
const getInterpretationTextFromChartResponse = (res) => {
const list = res?.data;
const first = Array.isArray(list) ? list[0] : null;
return (
first?.["解读"] ||
first?.["interpretation"] ||
first?.["analysis"] ||
first?.["content"] ||
""
);
};
const appendAiInterpretationChunk = (targetRef, chunk, loadingText = "解读生成中…") => {
if (!chunk) {
return;
}
const current = String(targetRef.value || "");
const base = current === loadingText ? "" : current;
targetRef.value = base + String(chunk);
};
const fetchBox5ChartInterpretation = async () => {
const list = Array.isArray(box5Data.value) ? box5Data.value : [];
if (!list.length) {
aiContentBox5.value = "暂无图表数据";
return;
}
const hasValidContent =
aiContentBox5.value &&
aiContentBox5.value !== "解读生成中…" &&
aiContentBox5.value !== "解读加载失败" &&
aiContentBox5.value !== "暂无图表数据";
if (hasValidContent || isBox5InterpretLoading.value) {
return;
}
isBox5InterpretLoading.value = true;
aiContentBox5.value = "解读生成中…";
const chartPayload = {
type: "词云图",
name: "报告关键词云",
data: list.map((item) => ({
name: item.name,
value: item.value
}))
};
try {
const res = await getChartAnalysis(
{ text: JSON.stringify(chartPayload) },
{
onChunk: chunk => {
appendAiInterpretationChunk(aiContentBox5, chunk);
}
}
);
const text = getInterpretationTextFromChartResponse(res);
aiContentBox5.value = text || aiContentBox5.value || "未返回有效解读内容";
} catch (error) {
console.error("报告关键词云图表解读请求失败", error);
aiContentBox5.value = "解读加载失败";
} finally {
isBox5InterpretLoading.value = false;
}
};
onMounted(() => {
handleGetThinkTankReportAbstract();
handleGetThinkTankReportViewpoint();
handleGetThinkTankReportIndustry();
handleGetThinkTankReportIndustryCloud();
});
</script>
<style lang="scss" scoped>
.wrap {
position: absolute;
inset: 0;
box-sizing: border-box;
width: 100%;
overflow: hidden;
.scroll-inner {
box-sizing: border-box;
width: 100%;
height: 100%;
min-height: 0;
padding-bottom: 16px;
overflow-x: hidden;
overflow-y: auto;
scrollbar-gutter: stable;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 8px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
}
}
.header {
width: 100%;
min-height: 94px;
box-sizing: border-box;
border-bottom: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
background: rgba(255, 255, 255, 1);
position: sticky;
top: 0;
z-index: 99999;
overflow: hidden;
.header-top {
margin: 0 auto;
margin-top: 20px;
width: 1600px;
display: flex;
justify-content: space-between;
.header-top-left {
display: flex;
img {
width: 54px;
height: 54px;
}
.title {
margin-left: 20px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 20px;
line-height: 26px;
letter-spacing: 0;
text-align: left;
color: rgb(59, 65, 75);
}
.en-title {
margin-left: 20px;
margin-top: 4px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 24px;
letter-spacing: 0;
text-align: left;
color: rgb(95, 101, 108);
}
}
.header-top-right {
display: flex;
flex-direction: column;
text-align: right;
align-items: flex-end;
.image-name-box {
width: 118px;
height: 24px;
gap: 6px;
text-align: right;
display: flex;
justify-content: flex-end;
.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;
}
.image {
width: 16px;
height: 16px;
margin-top: 5px;
img {
width: 100%;
height: 100%;
}
}
}
.tag-box {
margin-top: 7px;
display: flex;
gap: 8px;
}
}
}
}
.bottom-row {
flex-direction: row;
justify-content: center;
display: flex;
gap: 16px;
margin-bottom: 70px;
.left {
gap: 16px;
display: flex;
flex-direction: column;
.box1 {
width: 480px;
height: 415px;
margin-top: 17px;
.box1-main {
width: 480px;
height: 361px;
padding: 24px 24px 65px 24px;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
position: relative;
.box5Chart {
width: 100%;
height: 100%;
margin: 0 auto;
overflow: hidden;
}
.box1-footer {
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
bottom: 20px;
left: 32px;
}
.ai-wrap {
position: absolute;
bottom: 18px;
right: 0;
cursor: pointer;
}
.ai-content {
position: absolute;
bottom: 0;
right: 0;
min-width: 480px;
min-height: 156px;
}
}
}
.box2 {
width: 480px;
// 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: 436px;
margin-top: 5px;
margin-left: 23px;
.box2-item {
height: 103px;
width: 100%;
border-bottom: 1px solid rgba(234, 236, 238, 1);
border-top: 1px solid rgba(234, 236, 238, 1);
.box2-item-content {
width: 100%;
height: 90px;
margin-top: 7px;
display: flex;
.left {
width: 56px;
height: 74px;
margin-top: 8px;
img {
width: 100%;
height: 100%;
}
}
.right-content {
margin-left: 13px;
width: 365px;
height: 76px;
margin-top: 7px;
.report-title {
height: 48px;
font-family: "Source Han Sans CN";
font-weight: 700;
font-size: 16px;
line-height: 24px;
letter-spacing: 0;
text-align: left;
/* 👇 下面是 两行文本超出省略 核心代码 */
display: -webkit-box;
-webkit-line-clamp: 2;
/* 限制显示 2 行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
}
.report-footer {
margin-top: 4px;
height: 22px;
justify-content: space-between;
display: flex;
.report-time {
height: 22px;
width: 97px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 14px;
line-height: 22px;
letter-spacing: 0;
text-align: left;
color: rgb(95, 101, 108);
}
.report-footer-right {
height: 22px;
display: flex;
gap: 6px;
.footer-image {
width: 16px;
height: 16px;
margin-top: 3px;
img {
width: 100%;
height: 100%;
}
}
.think-name {
height: 22px;
}
}
}
}
}
}
}
.box2-btn {
margin-top: 16px;
margin-bottom: 21px;
margin-left: 23px;
width: 436px;
height: 36px;
background-color: rgb(5, 95, 194);
border-radius: 6px;
display: flex;
.btn-text {
color: rgb(255, 255, 255);
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 22px;
letter-spacing: 0;
margin-left: 120px;
margin-top: 7px;
}
.btn-image {
width: 13px;
height: 8px;
margin-left: 8px;
display: inline-block;
margin-top: 14px;
img {
width: 100%;
height: 100%;
display: block;
}
}
}
}
}
.right {
margin-top: 17px;
gap: 16px;
display: flex;
flex-direction: column;
.box3 {
width: 1103px;
// 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);
.box3-main {
width: 1058px;
margin-top: 3px;
margin-left: 22px;
padding-bottom: 22px;
:deep(.summary-main) {
margin-bottom: 25px;
font-family: "Source Han Sans CN";
font-weight: 400;
/* Regular 常规 */
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
color: rgb(59, 65, 75);
/* 两端对齐 */
}
.box3-top {
width: 1058px;
height: 48px;
background: linear-gradient(rgb(137, 193, 255, 0.1), rgb(255, 255, 255));
display: flex;
.top-title {
width: 1010px;
height: 32px;
margin-left: 24px;
margin-top: 16px;
.title-image {
width: 199px;
height: 32px;
img {
width: 100%;
height: 100%;
}
}
}
}
.box3-text {
width: 1006px;
margin-top: 24px;
margin-left: 26px;
font-family: "Source Han Sans CN";
font-weight: 400;
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: justify;
text-justify: inter-ideograph;
}
}
}
.box4 {
width: 1103px;
height: auto;
// 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;
.box4-main {
width: 1103px;
padding: 5px 24px 21px 28px;
.text {
font-family: "Source Han Sans CN";
font-weight: 400;
/* Regular */
font-size: 16px;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
color: rgb(59, 65, 75);
}
}
}
.box5 {
width: 1103px;
height: auto;
position: relative;
.box5-main {
width: 1103px;
padding: 3px 18px 21px 20px;
.box5-main-item-box {
width: 1065px;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px 0;
.item {
width: 532.5px;
height: 49px;
display: flex;
flex-direction: row;
.item-left {
width: 42px;
height: 42px;
border-radius: 50%;
margin-top: 3px;
margin-left: 3px;
img {
width: 100%;
height: 100%;
display: block;
border-radius: 50%;
/* 图片也要加,不然会漏出直角 */
object-fit: cover;
/* 防止图片变形 */
}
}
.item-right {
height: 49px;
margin-left: 11px;
display: flex;
flex-direction: column;
.item-name {
height: 24px;
margin-top: 1px;
font-family: "Source Han Sans CN";
font-weight: 700;
/* Bold 粗体 */
font-size: 16px;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
color: rgb(59, 65, 75);
}
.item-position {
height: 22px;
font-family: "Source Han Sans CN";
font-weight: 400;
/* Regular */
font-size: 14px;
line-height: 22px;
letter-spacing: 0;
text-align: left;
color: rgb(95, 101, 108);
}
}
}
}
}
}
}
}
}
:deep(.analysis-box-wrapper .wrapper-header) {
height: 54px !important;
display: flex;
align-items: center;
.header-title>div {
line-height: 54px;
}
}
</style>
...@@ -1221,7 +1221,7 @@ function mapPolicyRowToView(row) { ...@@ -1221,7 +1221,7 @@ function mapPolicyRowToView(row) {
const toDetail = item => { const toDetail = item => {
window.sessionStorage.setItem("curTabName", item.contentZh ?? item.content ?? ""); window.sessionStorage.setItem("curTabName", item.contentZh ?? item.content ?? "");
const route = router.resolve({ const route = router.resolve({
name: "ReportOriginal", name: "ReportDetail",
params: { params: {
id: item.reportId id: item.reportId
} }
......
...@@ -7,18 +7,11 @@ ...@@ -7,18 +7,11 @@
<div class="title">{{ "科技领域" }}</div> <div class="title">{{ "科技领域" }}</div>
</div> </div>
<div class="select-main"> <div class="select-main">
<el-checkbox-group <el-checkbox-group class="checkbox-group" :model-value="selectedAreaList" @change="handleAreaGroupChange">
class="checkbox-group"
:model-value="selectedAreaList"
@change="handleAreaGroupChange">
<el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_AREA"> <el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_AREA">
{{ RESOURCE_FILTER_ALL_AREA }} {{ RESOURCE_FILTER_ALL_AREA }}
</el-checkbox> </el-checkbox>
<el-checkbox <el-checkbox v-for="research in areaList" :key="research.id" class="filter-checkbox" :label="research.id">
v-for="research in areaList"
:key="research.id"
class="filter-checkbox"
:label="research.id">
{{ research.name }} {{ research.name }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
...@@ -31,18 +24,11 @@ ...@@ -31,18 +24,11 @@
<div class="title">{{ "发布时间" }}</div> <div class="title">{{ "发布时间" }}</div>
</div> </div>
<div class="select-main"> <div class="select-main">
<el-checkbox-group <el-checkbox-group class="checkbox-group" :model-value="selectedPubTimeList" @change="handleTimeGroupChange">
class="checkbox-group"
:model-value="selectedPubTimeList"
@change="handleTimeGroupChange">
<el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_TIME"> <el-checkbox class="filter-checkbox all-checkbox" :label="RESOURCE_FILTER_ALL_TIME">
{{ RESOURCE_FILTER_ALL_TIME }} {{ RESOURCE_FILTER_ALL_TIME }}
</el-checkbox> </el-checkbox>
<el-checkbox <el-checkbox v-for="time in pubTimeList" :key="time.id" class="filter-checkbox" :label="time.id">
v-for="time in pubTimeList"
:key="time.id"
class="filter-checkbox"
:label="time.id">
{{ time.name }} {{ time.name }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
......
...@@ -120,7 +120,7 @@ const handleOpenReportOriginal = (item) => { ...@@ -120,7 +120,7 @@ const handleOpenReportOriginal = (item) => {
const reportId = item?.reportId || item?.report_id || item?.id const reportId = item?.reportId || item?.report_id || item?.id
if (!reportId) return if (!reportId) return
const route = router.resolve({ const route = router.resolve({
name: "ReportOriginal", name: "ReportDetail",
params: { id: String(reportId) } params: { id: String(reportId) }
}) })
window.open(route.href, "_blank") window.open(route.href, "_blank")
......
...@@ -163,7 +163,35 @@ const getMultiLineChart = (data) => { ...@@ -163,7 +163,35 @@ const getMultiLineChart = (data) => {
{ {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: title data: title,
axisLabel: {
// 同时设置 color 与 textStyle,避免 ECharts/主题在 axisLine/axisTick 展示时覆盖
color: 'rgba(132, 136, 142, 1)',
textStyle: {
color: 'rgba(132, 136, 142, 1)',
fontSize: 14,
fontFamily: 'Source Han Sans CN',
fontWeight: 400
}
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(231, 241, 255, 1)' // 你要的红色
}
},
axisTick: {
show: true,
// 让刻度和 label 对齐,显示短直线
alignWithLabel: true,
length: 6,
length2: 0,
lineStyle: {
color: "rgba(231, 241, 255, 1)",
type: "solid"
}
},
} }
], ],
yAxis: [ yAxis: [
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论