提交 002c3693 authored 作者: 张烨's avatar 张烨

Merge remote-tracking branch 'origin/pre' into zy-dev

流水线 #374 已通过 于阶段
in 1 分 39 秒
...@@ -199,7 +199,6 @@ ...@@ -199,7 +199,6 @@
"resolved": "https://registry.npmmirror.com/@antv/g6/-/g6-4.8.25.tgz", "resolved": "https://registry.npmmirror.com/@antv/g6/-/g6-4.8.25.tgz",
"integrity": "sha512-8mdTnN9QMVNQZtlXmftL8fvRsa4L+GajK58Zp51wyrGLFyjeop8R0QSkCALW45DWP2TaQeZAPtjhQUU/wf5hIg==", "integrity": "sha512-8mdTnN9QMVNQZtlXmftL8fvRsa4L+GajK58Zp51wyrGLFyjeop8R0QSkCALW45DWP2TaQeZAPtjhQUU/wf5hIg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@antv/g6-pc": "0.8.25" "@antv/g6-pc": "0.8.25"
} }
...@@ -2172,7 +2171,6 @@ ...@@ -2172,7 +2171,6 @@
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz",
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/lodash": "*" "@types/lodash": "*"
} }
...@@ -2289,7 +2287,6 @@ ...@@ -2289,7 +2287,6 @@
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
"integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/parser": "^7.28.3", "@babel/parser": "^7.28.3",
"@vue/compiler-core": "3.5.21", "@vue/compiler-core": "3.5.21",
...@@ -2896,7 +2893,6 @@ ...@@ -2896,7 +2893,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.9.0", "baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759", "caniuse-lite": "^1.0.30001759",
...@@ -3255,7 +3251,6 @@ ...@@ -3255,7 +3251,6 @@
"resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz", "resolved": "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz",
"integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==",
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
} }
...@@ -3653,7 +3648,6 @@ ...@@ -3653,7 +3648,6 @@
"resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz", "resolved": "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"license": "ISC", "license": "ISC",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
...@@ -3742,7 +3736,6 @@ ...@@ -3742,7 +3736,6 @@
"version": "0.8.5", "version": "0.8.5",
"resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz", "resolved": "https://registry.npmmirror.com/dagre/-/dagre-0.8.5.tgz",
"integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==",
"peer": true,
"dependencies": { "dependencies": {
"graphlib": "^2.1.8", "graphlib": "^2.1.8",
"lodash": "^4.17.15" "lodash": "^4.17.15"
...@@ -3950,7 +3943,6 @@ ...@@ -3950,7 +3943,6 @@
"resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz", "resolved": "https://registry.npmmirror.com/echarts/-/echarts-5.6.0.tgz",
"integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==", "integrity": "sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"dependencies": { "dependencies": {
"tslib": "2.3.0", "tslib": "2.3.0",
"zrender": "5.6.1" "zrender": "5.6.1"
...@@ -5342,15 +5334,13 @@ ...@@ -5342,15 +5334,13 @@
"version": "4.18.1", "version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.18.1", "version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz",
"integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==", "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==",
"license": "MIT", "license": "MIT"
"peer": true
}, },
"node_modules/lodash-unified": { "node_modules/lodash-unified": {
"version": "1.0.3", "version": "1.0.3",
...@@ -5398,7 +5388,6 @@ ...@@ -5398,7 +5388,6 @@
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
"integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"argparse": "^2.0.1", "argparse": "^2.0.1",
"entities": "^4.4.0", "entities": "^4.4.0",
...@@ -6478,7 +6467,6 @@ ...@@ -6478,7 +6467,6 @@
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
...@@ -6575,7 +6563,6 @@ ...@@ -6575,7 +6563,6 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"nanoid": "^3.3.11", "nanoid": "^3.3.11",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
...@@ -6977,7 +6964,6 @@ ...@@ -6977,7 +6964,6 @@
"integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/estree": "1.0.8" "@types/estree": "1.0.8"
}, },
...@@ -8148,7 +8134,6 @@ ...@@ -8148,7 +8134,6 @@
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.21.3", "esbuild": "^0.21.3",
"postcss": "^8.4.43", "postcss": "^8.4.43",
...@@ -8208,7 +8193,6 @@ ...@@ -8208,7 +8193,6 @@
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.21.tgz", "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.21.tgz",
"integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vue/compiler-dom": "3.5.21", "@vue/compiler-dom": "3.5.21",
"@vue/compiler-sfc": "3.5.21", "@vue/compiler-sfc": "3.5.21",
......
...@@ -259,26 +259,7 @@ export async function getHistoryBillListWithStage(personId, params = {}) { ...@@ -259,26 +259,7 @@ export async function getHistoryBillListWithStage(personId, params = {}) {
} }
} }
// /**
// * 获取潜在提案举措分析
// * GET /api/personHomepage/historyBill/clause/{personId}
// * @param {string} personId - 人物ID
// * @param {Object} params - 查询参数
// */
// export async function getPotentialClauseAnalysis(personId, params = {}) {
// const queryString = Object.entries(params)
// .filter(([, value]) => value !== undefined && value !== null && value !== '')
// .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
// .join('&')
// const url = queryString
// ? `/api/personHomepage/historyBill/clause/${personId}?${queryString}`
// : `/api/personHomepage/historyBill/clause/${personId}`
// return request(url, {
// method: 'GET',
// })
// }
/** /**
* 获取排序选项 * 获取排序选项
...@@ -331,31 +312,32 @@ export function getProgressPrediction(billId) { ...@@ -331,31 +312,32 @@ export function getProgressPrediction(billId) {
}) })
} }
/** /**
* 获取相似法案列表 * 获取相似法案列表
* @param {Object} params - 查询参数 * @param {Object} params - 查询参数
* @param {string} params.billIds - 当前法案的ID * @param {string} params.billIds - 当前法案的ID
* @param {string[]} params.domains - 领域名称列表 * @param {string} params.domains - 领域名称,多个用逗号拼接
* @param {string} params.patternType - 政府结构类型,如 "统一政府"/"分裂政府"/"微弱多数" * @param {string} params.patternType - 政府结构类型
* @param {string} params.proposalType - 提案类型,如 "两党共同提案"/"单政党提案(共和党提案)"/"单政党提案(民主党提案)" * @param {string} params.proposalType - 提案类型
* @param {string} params.timeFrame - 时间范围(单个字符串)
* @param {number} params.maxNum - 返回最大数量
* @returns {Promise<Object>} 相似法案列表 * @returns {Promise<Object>} 相似法案列表
*/ */
export function getSimiBills(params = {}) { export function getSimiBills(params = {}) {
// domains 如果是数组则用逗号拼接
const domains = Array.isArray(params.domains)
? params.domains.join(',')
: params.domains
return request('/api/BillProgressPrediction/simiBills', { return request('/api/BillProgressPrediction/simiBills', {
method: 'GET', method: 'GET',
params: { params: {
billIds: params.billIds, billIds: params.billIds,
domains: domains, domains: params.domains,
patternType: params.patternType, patternType: params.patternType,
proposalType: params.proposalType proposalType: params.proposalType,
timeFrame: params.timeFrame,
maxNum: params.maxNum || 50
} }
}) })
} }
/** /**
* 格式化日期 YYYY-MM-DD -> YYYY年M月D日 * 格式化日期 YYYY-MM-DD -> YYYY年M月D日
* @param {string} dateStr - 日期字符串 * @param {string} dateStr - 日期字符串
......
...@@ -201,3 +201,16 @@ export async function getSubjectList(params) { ...@@ -201,3 +201,16 @@ export async function getSubjectList(params) {
params params
}) })
} }
/**
* 获取调研项目列表
* @param {string} personId
* @param {Object} params
*/
export function getInvestigationProject(personId, params) {
return request({
method: 'GET',
url: `/api/personHomepage/investigationProject/${personId}`,
params,
})
}
\ No newline at end of file
...@@ -393,13 +393,13 @@ export function getDomainDistribution(sanctionDate = "2025-11-11") { ...@@ -393,13 +393,13 @@ export function getDomainDistribution(sanctionDate = "2025-11-11") {
* startTime: string * startTime: string
* }[]>} * }[]>}
*/ */
export function getEntitiesList(sanTypeIds = ["1"], pageNum = 1, pageSize = 10, sanctionDate = "", isCn = false) { export function getEntitiesList(sanTypeId="1", pageNum = 1, pageSize = 10, sanctionDate = "", isCn = false) {
return request200( return request200(
request({ request({
method: "POST", method: "POST",
url: "/api/sanctionList/pageQuery", url: "/api/sanctionList/pageQuery",
data: { data: {
sanTypeIds, sanTypeId,
pageNum, pageNum,
pageSize, pageSize,
sanctionDate, sanctionDate,
......
import { createRouter, createWebHistory } from "vue-router"; import { createRouter, createWebHistory } from "vue-router";
import { getIsLoggedIn } from "@/utils/auth";
const Home = () => import('@/views/home/index.vue') const Home = () => import('@/views/home/index.vue')
const DataLibrary = () => import('@/views/dataLibrary/index.vue') const DataLibrary = () => import('@/views/dataLibrary/index.vue')
...@@ -21,6 +22,14 @@ const dataRoutes = Object.keys(datas).reduce((acc, path) => { ...@@ -21,6 +22,14 @@ const dataRoutes = Object.keys(datas).reduce((acc, path) => {
}, []) }, [])
const routes = [ const routes = [
{
path: "/login",
name: "Login",
component: () => import("@/views/login/index.vue"),
meta: {
title: "登录"
}
},
{ {
path: "/", path: "/",
name: "Home", name: "Home",
...@@ -66,6 +75,19 @@ const router = createRouter({ ...@@ -66,6 +75,19 @@ const router = createRouter({
// 路由守卫 - 设置页面标题 // 路由守卫 - 设置页面标题
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// 登录态:同一次前端服务 BOOT_ID 内跨刷新/跨新标签有效;服务重启后自动失效
const isAuthed = getIsLoggedIn();
const isLoginRoute = to.name === "Login" || /^\/login\/?$/.test(String(to.path || ""));
if (!isLoginRoute && !isAuthed) {
next({
path: "/login",
query: { redirect: to.fullPath }
});
return;
}
if (to.meta.title) { if (to.meta.title) {
if (to.meta.dynamicTitle) { if (to.meta.dynamicTitle) {
console.log('to', to); console.log('to', to);
......
...@@ -4,7 +4,7 @@ const ZMOverview = () => import('@/views/ZMOverView/index.vue') ...@@ -4,7 +4,7 @@ const ZMOverview = () => import('@/views/ZMOverView/index.vue')
const ZMOverviewRoutes = [ const ZMOverviewRoutes = [
{ {
path: "/", path: "/",
redirect: "/ZMOverView" redirect: "/login"
}, },
{ {
path: "/ZMOverView", path: "/ZMOverView",
......
...@@ -123,19 +123,9 @@ const billRoutes = [ ...@@ -123,19 +123,9 @@ const billRoutes = [
component: BillInfluenceScientificResearch, component: BillInfluenceScientificResearch,
// meta: { title: "对华科研影响" } // meta: { title: "对华科研影响" }
}, },
// {
// path: "ProgressForecast",
// name: "BillProgressForecast",
// component: BillProgressForecast,
// // meta: { title: "对华科研影响" }
// }
] ]
}, { },
path: "ProgressForecast/:id",
name: "BillProgressForecast",
component: BillProgressForecast,
// meta: { title: "对华科研影响" }
},
{ {
path: "relevantCircumstance", path: "relevantCircumstance",
...@@ -155,6 +145,14 @@ const billRoutes = [ ...@@ -155,6 +145,14 @@ const billRoutes = [
} }
] ]
}, },
{
path: "ProgressForecast/:id",
name: "BillProgressForecast",
component: BillProgressForecast,
meta: {
title: "法案预测"
}
},
] ]
export default billRoutes export default billRoutes
\ No newline at end of file
...@@ -16,7 +16,7 @@ const financeRoutes = [ ...@@ -16,7 +16,7 @@ const financeRoutes = [
{ {
path: "/finance/sdnlistoverview", path: "/finance/sdnlistoverview",
name: "sdnlistOverview", name: "sdnlistOverview",
component: () => import("@/views/finance/v2.0EntityList/index.vue"), component: () => import("@/views/finance/entityList/index.vue"),
meta: { meta: {
title: "SDN制裁清单概览" title: "SDN制裁清单概览"
} }
...@@ -24,8 +24,8 @@ const financeRoutes = [ ...@@ -24,8 +24,8 @@ const financeRoutes = [
// V2.0单条制裁详情 // V2.0单条制裁详情
{ {
path: "/finance/singleSanction", path: "/finance/singleSanction",
name: "singleSanction", name: "sdnSingleSanction",
component: () => import("@/views/finance/v2.0SingleSanction/index.vue"), component: () => import("@/views/finance/singleSanction/index.vue"),
meta: { meta: {
title: "单条制裁详情", title: "单条制裁详情",
dynamicTitle: true dynamicTitle: true
...@@ -34,8 +34,8 @@ const financeRoutes = [ ...@@ -34,8 +34,8 @@ const financeRoutes = [
// V2.0单条制裁详情-实体清单原文 // V2.0单条制裁详情-实体清单原文
{ {
path: "/exportControl/origin", path: "/exportControl/origin",
name: "entityListOrigin", name: "financeEntityListOrigin",
component: () => import("@/views/exportControl/v2.0SingleSanction/originPage/index.vue") component: () => import("@/views/finance/singleSanction/originPage/index.vue")
// meta: { // meta: {
// title: "实体清单原文" // title: "实体清单原文"
// } // }
...@@ -44,7 +44,7 @@ const financeRoutes = [ ...@@ -44,7 +44,7 @@ const financeRoutes = [
{ {
path: "/finance/cmccontrolList", path: "/finance/cmccontrolList",
name: "cmccontrolList", name: "cmccontrolList",
component: () => import("@/views/finance/v2.0CommercialControlList/index.vue"), component: () => import("@/views/finance/commercialControlList/index.vue"),
meta: { meta: {
title: "涉军企业清单概览" title: "涉军企业清单概览"
} }
......
// 规则限制 // 规则限制
const RuleRestriction = () => import('@/views/ruleRestriction/index.vue') const RuleRestriction = () => import('@/views/ruleRestriction/index.vue')
const RuleRestrictionDetail = () => import('@/views/ruleRestriction/detail/index.vue') const RuleRestrictionDetail = () => import('@/views/ruleRestriction/detail/index.vue')
const RuleRestrictionsAlliance = () => import('@/views/ruleRestriction/alliance/index.vue')
const ruleRestrictionsRoutes = [ const ruleRestrictionsRoutes = [
// 规则限制 // 规则限制
{ {
...@@ -22,6 +23,14 @@ const ruleRestrictionsRoutes = [ ...@@ -22,6 +23,14 @@ const ruleRestrictionsRoutes = [
title: "规则限制详情", title: "规则限制详情",
dynamicTitle: true dynamicTitle: true
} }
}, {
path: "/ruleRestrictions/alliance",
name: "RuleRestrictionsAlliance",
component: RuleRestrictionsAlliance,
meta: {
title: "规则限制联盟详情",
dynamicTitle: true
}
}, },
] ]
......
const STORAGE_LOGIN_FLAG = "isLoggedIn";
const STORAGE_LOGIN_BOOT_ID = "loginBootId";
const getBootId = () => {
// 由 vite.config.js 的 define 注入:重启 dev server 会变化
try {
// eslint-disable-next-line no-undef
return String(__APP_BOOT_ID__ || "");
} catch (_) {
return "";
}
};
export const getIsLoggedIn = () => {
try {
const flag = window.localStorage.getItem(STORAGE_LOGIN_FLAG) === "1";
const savedBootId = window.localStorage.getItem(STORAGE_LOGIN_BOOT_ID) || "";
return flag && savedBootId && savedBootId === getBootId();
} catch (_) {
return false;
}
};
export const setIsLoggedIn = (val) => {
try {
if (val) {
window.localStorage.setItem(STORAGE_LOGIN_FLAG, "1");
window.localStorage.setItem(STORAGE_LOGIN_BOOT_ID, getBootId());
} else {
window.localStorage.removeItem(STORAGE_LOGIN_FLAG);
window.localStorage.removeItem(STORAGE_LOGIN_BOOT_ID);
}
} catch (_) {
// ignore
}
};
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
<DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader> <DivideHeader id="position2" class="divide2" :titleText="'资讯要闻'"></DivideHeader>
<div class="center-center"> <div class="center-center">
<NewsList :newsList="newsList" img="newsImage" title="newsTitle" from="from" content="newsContent" <NewsList :newsList="newsList" img="newsImage" title="newsTitle" from="from" content="newsContent"
@item-click="handleClickNewsDetail" /> @item-click="handleClickNewsDetail" @more-click="handleToMoreNews" />
<MessageBubble :messageList="messageList" imageUrl="personImage" @more-click="handleToSocialDetail" <MessageBubble :messageList="messageList" imageUrl="personImage" @more-click="handleToSocialDetail"
@person-click="handleClickToCharacter" name="personName" content="remarks" source="orgName" /> @person-click="handleClickToCharacter" name="personName" content="remarks" source="orgName" />
</div> </div>
...@@ -297,22 +297,26 @@ import { ElMessage } from "element-plus"; ...@@ -297,22 +297,26 @@ import { ElMessage } from "element-plus";
import { Calendar } from "@element-plus/icons-vue"; import { Calendar } from "@element-plus/icons-vue";
import { useGotoNewsDetail } from "@/router/modules/news"; import { useGotoNewsDetail } from "@/router/modules/news";
// 跳转人物主页 // 跳转人物主页(MessageBubble 的 person-click 传入整条列表项,需取 personId)
const handleClickToCharacter = async (id, name) => { const handleClickToCharacter = async item => {
if (!item?.personId) {
ElMessage.warning("缺少人员信息,无法跳转");
return;
}
const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList")); const personTypeList = JSON.parse(window.sessionStorage.getItem("personTypeList"));
let type = 0; let type = 0;
let personTypeName = ""; let personTypeName = "";
const params = { const params = {
personId: id personId: item.personId
}; };
try { try {
const res = await getPersonSummaryInfo(params); const res = await getPersonSummaryInfo(params);
console.log("人物全局信息2", res); console.log("人物全局信息2", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const arr = personTypeList.filter(item => { const arr = personTypeList.filter(pt => {
return item.typeId === res.data.personType; return pt.typeId === res.data.personType;
}); });
console.log("arr", arr); console.log("arr", arr);
...@@ -331,12 +335,12 @@ const handleClickToCharacter = async (id, name) => { ...@@ -331,12 +335,12 @@ const handleClickToCharacter = async (id, name) => {
ElMessage.warning("找不到当前人员的类型值!"); ElMessage.warning("找不到当前人员的类型值!");
return; return;
} }
window.sessionStorage.setItem("curTabName", name); window.sessionStorage.setItem("curTabName", item.personName);
const route = router.resolve({ const route = router.resolve({
path: "/characterPage", path: "/characterPage",
query: { query: {
type: type, // type=1为科技企业领袖,2为国会议员,3为智库研究人员 type: type, // type=1为科技企业领袖,2为国会议员,3为智库研究人员
personId: id personId: item.personId
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
...@@ -458,6 +462,12 @@ const handleToMoreRiskSignal = () => { ...@@ -458,6 +462,12 @@ const handleToMoreRiskSignal = () => {
window.open(route.href, "_blank"); window.open(route.href, "_blank");
// router.push("/viewRiskSignal") // router.push("/viewRiskSignal")
}; };
// 查看更多新闻资讯(新闻主页)
const handleToMoreNews = () => {
const route = router.resolve("/newsBrief");
window.open(route.href, "_blank");
};
// 风险信号 // 风险信号
const warningList = ref([]); const warningList = ref([]);
......
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/bill-tokens.scss'; @use '@/styles/bill-tokens.scss' as *;
.bill-page-shell { .bill-page-shell {
width: 100%; width: 100%;
......
...@@ -87,7 +87,7 @@ export default { ...@@ -87,7 +87,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/bill-tokens.scss'; @use '@/styles/bill-tokens.scss' as *;
.bill-panel { .bill-panel {
width: 100%; width: 100%;
......
...@@ -93,7 +93,7 @@ export default { ...@@ -93,7 +93,7 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@import '@/styles/bill-tokens.scss'; @use '@/styles/bill-tokens.scss' as *;
.bill-two-column { .bill-two-column {
width: 100%; width: 100%;
......
<template> <template>
<div class="action-buttons"> <div class="action-buttons">
<button class="btn-secondary" @click="emit('reset')"> <div class="action-left">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <button class="btn-secondary" @click="emit('reset')">
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /> <svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M3 3v5h5" /> <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
</svg> <path d="M3 3v5h5" />
<span>重置筛选条件</span> </svg>
</button> <span>重置筛选条件</span>
</button>
<button v-if="showSetAsCurrent" class="btn-secondary" @click="emit('setAsCurrent')">
<span>设置为当前提案</span>
</button>
</div>
<div class="action-right"> <div class="action-right">
<button <button
v-if="showPrevious" v-if="showPrevious"
...@@ -36,12 +41,14 @@ defineProps<{ ...@@ -36,12 +41,14 @@ defineProps<{
previousText?: string previousText?: string
nextText?: string nextText?: string
nextDisabled?: boolean nextDisabled?: boolean
showSetAsCurrent?: boolean
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
reset: [] reset: []
previous: [] previous: []
next: [] next: []
setAsCurrent: []
}>() }>()
</script> </script>
...@@ -53,6 +60,12 @@ const emit = defineEmits<{ ...@@ -53,6 +60,12 @@ const emit = defineEmits<{
padding-top: 24px; padding-top: 24px;
} }
.action-left {
display: flex;
align-items: center;
gap: 12px;
}
.action-right { .action-right {
display: flex; display: flex;
align-items: center; align-items: center;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<img src="../assets/fitller.svg" /> <img src="../assets/fitller.svg" />
<h2 class="section-title text-title-3-bold">核心相似度维度筛选</h2> <h2 class="section-title text-title-3-bold">核心相似度维度筛选</h2>
</div> </div>
<button class="btn-outline" @click="setAsCurrent">设置为当前提案</button>
</div> </div>
<div class="divider" /> <div class="divider" />
<div class="fields-grid"> <div class="fields-grid">
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
<div class="field-content"> <div class="field-content">
<el-select <el-select
v-model="localValues.oppositionProposer" v-model="localValues.oppositionProposer"
multiple
placeholder="请选择" placeholder="请选择"
style="width: 420px" style="width: 420px"
@change="handleChange" @change="handleChange"
...@@ -109,21 +110,21 @@ const props = defineProps<{ ...@@ -109,21 +110,21 @@ const props = defineProps<{
defaultFilters?: Record<string, string[]> defaultFilters?: Record<string, string[]>
}>() }>()
// 本地 v-model 值 // 本地 v-model 值(proposalTime 为单选,用 string)
const localValues = ref({ const localValues = ref({
policyArea: [] as string[], policyArea: [] as string[],
governmentType: [] as string[], governmentType: [] as string[],
oppositionProposer: '' as string, oppositionProposer: [] as string[],
proposalTime: '' as string, proposalTime: '' as string,
}) })
// 根据 proposalInfo 计算初始筛选值(即"设置为当前提案"的目标状态) // 根据 proposalInfo 计算初始筛选值(即"设置为当前提案"的目标状态)
function buildInitialValues(info?: ProposalInfo | null): Record<string, string[] | string> { function buildInitialValues(info?: ProposalInfo | null) {
if (!info) return { policyArea: [], governmentType: [], oppositionProposer: '', proposalTime: '' } if (!info) return { policyArea: [], governmentType: [], oppositionProposer: [], proposalTime: '' }
return { return {
policyArea: info.defaultDomains?.length ? [...info.defaultDomains] : [...(info.areas || [])], policyArea: info.defaultDomains?.length ? [...info.defaultDomains] : [...(info.areas || [])],
governmentType: info.patternType ? [info.patternType] : [], governmentType: info.patternType ? [info.patternType] : [],
oppositionProposer: '', oppositionProposer: [],
proposalTime: '', proposalTime: '',
} }
} }
...@@ -142,7 +143,7 @@ function reset() { ...@@ -142,7 +143,7 @@ function reset() {
localValues.value = { localValues.value = {
policyArea: [], policyArea: [],
governmentType: [], governmentType: [],
oppositionProposer: '', oppositionProposer: [],
proposalTime: '', proposalTime: '',
} }
} }
...@@ -152,14 +153,39 @@ function setAsCurrent() { ...@@ -152,14 +153,39 @@ function setAsCurrent() {
Object.assign(localValues.value, buildInitialValues(props.proposalInfo)) Object.assign(localValues.value, buildInitialValues(props.proposalInfo))
} }
defineExpose({ reset, setAsCurrent, getValues: () => localValues.value }) defineExpose({
reset,
setAsCurrent,
getValues: () => ({
...localValues.value,
// proposalTime 单选,包装为数组保持传值一致
proposalTime: localValues.value.proposalTime ? [localValues.value.proposalTime] : []
})
})
// 固定的政策领域选项(15个)
const policyAreaOptions = [
{ value: '人工智能', label: '人工智能' },
{ value: '生物科技', label: '生物科技' },
{ value: '新一代通信网络', label: '新一代通信网络' },
{ value: '量子科技', label: '量子科技' },
{ value: '新能源', label: '新能源' },
{ value: '集成电路', label: '集成电路' },
{ value: '海洋', label: '海洋' },
{ value: '先进制造', label: '先进制造' },
{ value: '新材料', label: '新材料' },
{ value: '航空航天', label: '航空航天' },
{ value: '太空', label: '太空' },
{ value: '深海', label: '深海' },
{ value: '极地', label: '极地' },
{ value: '核', label: '核' },
{ value: '其他', label: '其他' }
]
// 固定的字段配置,options 由 proposalInfo 动态注入 // 固定的字段配置
const fields = computed(() => { const fields = computed(() => {
const billDomain = props.proposalInfo?.billDomain
const domainOptions = Array.isArray(billDomain) ? billDomain.map(d => ({ value: d, label: d })) : []
return { return {
policyArea: { options: domainOptions }, policyArea: { options: policyAreaOptions },
governmentType: { governmentType: {
options: [ options: [
{ value: '统一政府', label: '统一政府' }, { value: '统一政府', label: '统一政府' },
...@@ -177,9 +203,9 @@ const fields = computed(() => { ...@@ -177,9 +203,9 @@ const fields = computed(() => {
}, },
proposalTime: { proposalTime: {
options: [ options: [
{ value: '近三年', label: '近三年' },
{ value: '近五年', label: '近五年' }, { value: '近五年', label: '近五年' },
{ value: '近十年', label: '近十年' }, { value: '近十年', label: '近十年' }
{ value: '全部', label: '全部' }
] ]
} }
} }
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
<div class="header-content"> <div class="header-content">
<div class="header-left"> <div class="header-left">
<div class="text-title-2-bold">科技法案立法进展预测</div> <div class="text-title-2-bold">科技法案立法进展预测</div>
<div class="text-tip-2 text-primary-65-clor">H.R.1-大而美法案(2025年5月20日)</div> <div class="text-tip-2 text-primary-65-clor" v-if="props.proposalInfo">{{ props.proposalInfo.title+"("+ props.proposalInfo.date+")" }}</div>
</div> </div>
<div class="header-actions"> <div class="header-actions">
<button class="action-btn"> <button class="action-btn">
...@@ -30,6 +30,11 @@ ...@@ -30,6 +30,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { ProposalInfo } from '../api'
const props = defineProps<{
proposalInfo?: ProposalInfo | null
defaultFilters?: Record<string, string[]>
}>()
</script> </script>
<style scoped> <style scoped>
...@@ -45,6 +50,7 @@ ...@@ -45,6 +50,7 @@
background: #ffffff; background: #ffffff;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
margin-bottom: 16px; margin-bottom: 16px;
margin-left: 0px;
} }
.header-content { .header-content {
......
...@@ -13,7 +13,9 @@ ...@@ -13,7 +13,9 @@
/> />
</div> </div>
<ActionButtons <ActionButtons
show-set-as-current
@reset="handleReset" @reset="handleReset"
@set-as-current="handleSetAsCurrent"
@next="handleNext" @next="handleNext"
/> />
</template> </template>
...@@ -65,6 +67,11 @@ function handleReset() { ...@@ -65,6 +67,11 @@ function handleReset() {
filterSectionRef.value?.reset() filterSectionRef.value?.reset()
} }
// 设置为当前提案:恢复初始状态
function handleSetAsCurrent() {
filterSectionRef.value?.setAsCurrent()
}
// 下一步:获取已选的筛选条件并传给父组件 // 下一步:获取已选的筛选条件并传给父组件
function handleNext() { function handleNext() {
const selectedFilters = filterSectionRef.value?.getValues() || {} const selectedFilters = filterSectionRef.value?.getValues() || {}
......
...@@ -122,26 +122,27 @@ const selectedCount = computed(() => { ...@@ -122,26 +122,27 @@ const selectedCount = computed(() => {
async function loadData() { async function loadData() {
loading.value = true loading.value = true
try { try {
// 使用真实 API,传入 billIds、domains、patternType、proposalType // 标准 API 参数格式
const params = { const params = {
billIds: filterParams?.value.billIds, billIds: filterParams?.value.billIds,
domains: filterParams?.value.domains || [], domains: (filterParams?.value.domains || []).join(','),
patternType: filterParams?.value.patternType || '统一政府', patternType: filterParams?.value.patternType || '统一政府',
proposalType: filterParams?.value.proposalType || '两党共同提案' proposalType: filterParams?.value.proposalType || '两党共同提案',
timeFrame: filterParams?.value.timeFrame?.[0] || '',
maxNum: 50
} }
const response = await getSimiBills(params) const response = await getSimiBills(params)
if (response && response.data) { if (response && response.data) {
// 保存原始数据(新 API 返回结构中 simi_bills 是法案数组) // 保存原始数据
rawBillsData.value = response.data.simi_bills || [] rawBillsData.value = response.data
const { stats: apiStats, bills: apiBills } = transformSimiBillsData(response) const { stats: apiStats, bills: apiBills } = transformSimiBillsData(response)
stats.value = apiStats stats.value = apiStats
bills.value = apiBills bills.value = apiBills
} else { } else {
stats.value = null stats.value = null
bills.value = [] bills.value = []
rawBillsData.value = []
} }
} catch (error) { } catch (error) {
console.error('获取相似法案失败:', error) console.error('获取相似法案失败:', error)
...@@ -187,8 +188,9 @@ function handleBack() { ...@@ -187,8 +188,9 @@ function handleBack() {
function handleStartAnalysis() { function handleStartAnalysis() {
// 获取用户勾选的法案ID列表 // 获取用户勾选的法案ID列表
const selectedIds = bills.value.filter(b => b.selected).map(b => b.id) const selectedIds = bills.value.filter(b => b.selected).map(b => b.id)
// 从原始数据中筛选出用户勾选的法案 // 从原始数据中筛选出用户勾选的法案(确保 rawBillsData 是数组)
const selectedBills = rawBillsData.value.filter(b => selectedIds.includes(b.bill_id)) const rawData = Array.isArray(rawBillsData.value) ? rawBillsData.value : []
const selectedBills = rawData.filter(b => selectedIds.includes(b.bill_id))
emit('next', selectedBills) emit('next', selectedBills)
} }
</script> </script>
...@@ -210,12 +212,16 @@ function handleStartAnalysis() { ...@@ -210,12 +212,16 @@ function handleStartAnalysis() {
.content-wrapper { .content-wrapper {
flex: 1; flex: 1;
overflow: auto; overflow-y: auto;
display: flex;
flex-direction: column;
min-height: 0;
} }
.stats-section { .stats-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%;
} }
.stats-header { .stats-header {
...@@ -333,6 +339,10 @@ function handleStartAnalysis() { ...@@ -333,6 +339,10 @@ function handleStartAnalysis() {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
flex: 1;
overflow-y: auto;
min-height: 0;
padding-bottom: 16px;
} }
.action-footer { .action-footer {
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
<div class="highlight-wave text-regular"></div> <div class="highlight-wave text-regular"></div>
<div class="highlight-content text-regular"> <div class="highlight-content text-regular">
<p class="highlight-title text-regular">该法案的通过概率为 {{ predictionData?.overallProbability || '—' }}</p> <p class="highlight-title text-regular">该法案的通过概率为 {{ predictionData?.overallProbability || '—' }}</p>
<!-- {{ predictionData.factorAnalysis }} -->
<p class="highlight-text text-regular" v-for="value in predictionData.factorAnalysis" :key="value">{{ '*'+value }}</p>
<p class="highlight-text text-regular">{{ predictionData?.highlightText }}</p> <p class="highlight-text text-regular">{{ predictionData?.highlightText }}</p>
</div> </div>
</div> </div>
......
...@@ -78,12 +78,19 @@ ...@@ -78,12 +78,19 @@
<div class="introduction-wrap-right-main"> <div class="introduction-wrap-right-main">
<div class="right-main-box1"> <div class="right-main-box1">
<div class="name-box"> <div class="name-box">
<div class="person-box"> <el-select
<div class="person-item" :class="{ nameItemActive: box3BtnActive === item.name }" v-model="box3BtnActive"
@click="handleClcikBox3Btn(item.name, index)" v-for="(item, index) in personList" :key="index"> class="person-select"
{{ item.name }} placeholder="请选择提出人"
</div> @change="handleClcikBox3Btn"
</div> >
<el-option
v-for="item in personList"
:key="item.id || item.name"
:label="item.name"
:value="item.name"
/>
</el-select>
</div> </div>
<div class="info-box"> <div class="info-box">
<div class="info-left"> <div class="info-left">
...@@ -302,9 +309,10 @@ const personList = ref([]); ...@@ -302,9 +309,10 @@ const personList = ref([]);
const curPerson = ref({}); const curPerson = ref({});
const box3BtnActive = ref(""); const box3BtnActive = ref("");
const handleClcikBox3Btn = (name, index) => { const handleClcikBox3Btn = name => {
box3BtnActive.value = name; box3BtnActive.value = name;
curPerson.value = personList.value[index]; const targetPerson = personList.value.find(item => item.name === name);
curPerson.value = targetPerson || {};
}; };
// 法案提出人 // 法案提出人
...@@ -800,59 +808,8 @@ onMounted(() => { ...@@ -800,59 +808,8 @@ onMounted(() => {
margin-left: 22px; margin-left: 22px;
margin-top: 10px; margin-top: 10px;
.person-box { .person-select {
width: 500px; width: 100%;
overflow-x: hidden;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
padding-bottom: 5px;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
border-radius: 4px;
background: #e1e1e1;
}
&::-webkit-scrollbar-track {
background: transparent;
}
.person-item {
min-height: 28px;
height: auto;
box-sizing: border-box;
border: 1px solid var(--btn-plain-border-color);
border-radius: 4px;
background: var(--btn-plain-bg-color);
color: var(--btn-plain-text-color);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin-right: 8px;
padding: 1px 12px;
cursor: pointer;
white-space: normal;
word-break: break-all;
line-height: 18px;
flex-shrink: 1;
max-width: 170px;
text-align: center;
margin-bottom: 10px;
}
.nameItemActive {
border: 1px solid var(--btn-active-border-color);
background: var(--btn-active-bg-color);
color: var(--btn-active-text-color);
}
} }
} }
......
...@@ -22,6 +22,12 @@ ...@@ -22,6 +22,12 @@
<span class="meta-value sponsor-name" :title="allSponsorNames"> <span class="meta-value sponsor-name" :title="allSponsorNames">
{{ firstSponsorName }} {{ firstSponsorName }}
</span> </span>
</div>
<div class="meta-row">
<span class="meta-label">委员会:</span>
<span class="meta-value sponsor-name" :title="allSponsorNames">
{{ firstSponsorName }}
</span>
</div> </div>
<div class="meta-row"> <div class="meta-row">
<span class="meta-label">相关领域:</span> <span class="meta-label">相关领域:</span>
...@@ -114,6 +120,7 @@ const currentStageIndex = computed(() => { ...@@ -114,6 +120,7 @@ const currentStageIndex = computed(() => {
gap: 13px; gap: 13px;
align-items: center; align-items: center;
height: 100%; height: 100%;
margin:15px 1px
} }
.bill-card-preview { .bill-card-preview {
......
...@@ -23,9 +23,10 @@ ...@@ -23,9 +23,10 @@
<template v-else> <template v-else>
<BillCard <BillCard
v-for="bill in bills" v-for="bill in bills"
:key="bill.id" :key="bill.billId"
:bill="bill" :bill="bill"
:progress-stages="progressStages" :progress-stages="progressStages"
@click="handleBillMoreClick(bill.billId)"
/> />
<div v-if="total > 0" class="bill-pagination"> <div v-if="total > 0" class="bill-pagination">
...@@ -46,7 +47,10 @@ ...@@ -46,7 +47,10 @@
<script setup> <script setup>
import BillCard from './BillCard.vue' import BillCard from './BillCard.vue'
import { useRouter } from "vue-router";
const router = useRouter();
import { useRoute } from "vue-router";
const route = useRoute();
defineProps({ defineProps({
bills: { type: Array, required: true }, bills: { type: Array, required: true },
loading: { type: Boolean, default: false }, loading: { type: Boolean, default: false },
...@@ -56,27 +60,45 @@ defineProps({ ...@@ -56,27 +60,45 @@ defineProps({
progressStages: { type: Array, default: () => ['提出', '众议院通过', '参议院通过', '分歧协调', '提交总统', '法案通过'] }, progressStages: { type: Array, default: () => ['提出', '众议院通过', '参议院通过', '分歧协调', '提交总统', '法案通过'] },
}) })
defineEmits(['page-change']) defineEmits(['page-change'])
/** 政策建议关联法案:新标签页打开法案介绍页,billId 随接口 id 变化 */
const handleBillMoreClick = (bill) => {
const billId = bill;
// debugger
if (!billId) {
return;
}
const route = router.resolve({
path: "/billLayout/bill/introduction",
query: { billId: String(billId) }
});
window.open(route.href, "_blank");
};
</script> </script>
<style scoped> <style scoped>
/* Bill List */
.bill-list { .bill-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
} }
.bill-list-loading { .bill-list-loading {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 16px;
} }
.bill-list-skeleton { .bill-list-skeleton {
display: flex; display: flex;
gap: 24px; gap: 24px;
background: #fff; background: var(--bg-white-100);
border: 1px solid #ebedf0; border-radius: 10px;
border-radius: 8px; box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
padding: 24px; padding: 24px;
} }
.skeleton-left { .skeleton-left {
width: 120px; width: 120px;
height: 190px; height: 190px;
...@@ -84,30 +106,35 @@ defineEmits(['page-change']) ...@@ -84,30 +106,35 @@ defineEmits(['page-change'])
border-radius: 4px; border-radius: 4px;
animation: pulse 1.5s ease-in-out infinite; animation: pulse 1.5s ease-in-out infinite;
} }
.skeleton-right { .skeleton-right {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px; gap: 12px;
} }
.skeleton-line { .skeleton-line {
height: 16px; height: 16px;
background: #f0f0f0; background: #f0f0f0;
border-radius: 4px; border-radius: 4px;
animation: pulse 1.5s ease-in-out infinite; animation: pulse 1.5s ease-in-out infinite;
} }
.skeleton-line.w30 { width: 30%; } .skeleton-line.w30 { width: 30%; }
.skeleton-line.w40 { width: 40%; } .skeleton-line.w40 { width: 40%; }
.skeleton-line.w50 { width: 50%; } .skeleton-line.w50 { width: 50%; }
.skeleton-line.w60 { width: 60%; } .skeleton-line.w60 { width: 60%; }
.skeleton-divider { .skeleton-divider {
height: 1px; height: 1px;
background: #ebedf0; background: #eaeced;
} }
.bill-list-empty { .bill-list-empty {
background: #fff; background: var(--bg-white-100);
border: 1px solid #ebedf0; border-radius: 10px;
border-radius: 8px; box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
padding: 48px; padding: 48px;
text-align: center; text-align: center;
color: #bfbfbf; color: #bfbfbf;
...@@ -117,6 +144,7 @@ defineEmits(['page-change']) ...@@ -117,6 +144,7 @@ defineEmits(['page-change'])
align-items: center; align-items: center;
gap: 12px; gap: 12px;
} }
@keyframes pulse { @keyframes pulse {
0%, 100% { opacity: 1; } 0%, 100% { opacity: 1; }
50% { opacity: 0.5; } 50% { opacity: 0.5; }
...@@ -129,6 +157,7 @@ defineEmits(['page-change']) ...@@ -129,6 +157,7 @@ defineEmits(['page-change'])
justify-content: space-between; justify-content: space-between;
padding: 16px 0 0; padding: 16px 0 0;
} }
.bill-pagination-total { .bill-pagination-total {
font-size: 14px; font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif; font-family: 'Microsoft YaHei', sans-serif;
...@@ -139,10 +168,11 @@ defineEmits(['page-change']) ...@@ -139,10 +168,11 @@ defineEmits(['page-change'])
/* Override el-pagination to match design */ /* Override el-pagination to match design */
.bill-pagination :deep(.el-pagination) { .bill-pagination :deep(.el-pagination) {
--el-pagination-bg-color: #fff; --el-pagination-bg-color: var(--bg-white-100);
--el-pagination-hover-color: rgba(5, 95, 194, 1); --el-pagination-hover-color: rgba(5, 95, 194, 1);
padding: 0; padding: 0;
} }
.bill-pagination :deep(.el-pagination.is-background .btn-prev), .bill-pagination :deep(.el-pagination.is-background .btn-prev),
.bill-pagination :deep(.el-pagination.is-background .btn-next), .bill-pagination :deep(.el-pagination.is-background .btn-next),
.bill-pagination :deep(.el-pagination.is-background .el-pager li) { .bill-pagination :deep(.el-pagination.is-background .el-pager li) {
...@@ -151,20 +181,23 @@ defineEmits(['page-change']) ...@@ -151,20 +181,23 @@ defineEmits(['page-change'])
line-height: 32px; line-height: 32px;
border-radius: 4px; border-radius: 4px;
border: 1px solid rgba(234, 236, 238, 1); border: 1px solid rgba(234, 236, 238, 1);
background: #fff; background: var(--bg-white-100);
color: rgba(59, 65, 75, 1); color: rgba(59, 65, 75, 1);
font-size: 14px; font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif; font-family: 'Microsoft YaHei', sans-serif;
margin: 0 3px; margin: 0 3px;
} }
.bill-pagination :deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) { .bill-pagination :deep(.el-pagination.is-background .el-pager li:not(.is-disabled).is-active) {
background: #fff; background: var(--bg-white-100);
color: rgba(5, 95, 194, 1); color: rgba(5, 95, 194, 1);
border-color: rgba(5, 95, 194, 1); border-color: rgba(5, 95, 194, 1);
} }
.bill-pagination :deep(.el-pagination.is-background .el-pager li:not(.is-disabled):hover) { .bill-pagination :deep(.el-pagination.is-background .el-pager li:not(.is-disabled):hover) {
color: rgba(5, 95, 194, 1); color: rgba(5, 95, 194, 1);
} }
.bill-pagination :deep(.el-pagination.is-background .btn-prev:hover), .bill-pagination :deep(.el-pagination.is-background .btn-prev:hover),
.bill-pagination :deep(.el-pagination.is-background .btn-next:hover) { .bill-pagination :deep(.el-pagination.is-background .btn-next:hover) {
color: rgba(5, 95, 194, 1); color: rgba(5, 95, 194, 1);
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
</div> </div>
<div class="doc-info"> <div class="doc-info">
<div class="doc-number">{{ billNumber }}</div> <div class="doc-number">{{ billNumber }}</div>
<div class="doc-serial">{{ billSerial }}</div> <div class="doc-serial" :title="billSerial">{{ billSerial }}</div>
</div> </div>
</div> </div>
</template> </template>
...@@ -147,5 +147,13 @@ defineProps({ ...@@ -147,5 +147,13 @@ defineProps({
color: rgba(95, 101, 108, 1); color: rgba(95, 101, 108, 1);
line-height: 24px; line-height: 24px;
text-align: center; text-align: center;
width:15em;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 6px 10px;
font-size: 14px;
cursor: pointer;
} }
</style> </style>
<template> <template>
<div class="news-pagination-bar"> <div class="news-pagination-bar" v-if="total>1">
<span class="news-pagination-total">{{ total }}篇新闻报告</span> <span class="news-pagination-total">{{ total }}篇新闻报告</span>
<div class="news-pagination"> <div class="news-pagination">
<button class="pg-btn" :disabled="currentPage <= 1" @click="$emit('update:currentPage', currentPage - 1)"> <button class="pg-btn" :disabled="currentPage <= 1" @click="$emit('update:currentPage', currentPage - 1)">
......
...@@ -20,11 +20,11 @@ ...@@ -20,11 +20,11 @@
<button <button
:class="['news-tab', { active: activeTab === 'local' }]" :class="['news-tab', { active: activeTab === 'local' }]"
@click="$emit('update:activeTab', 'local')" @click="$emit('update:activeTab', 'local')"
>智库报告</button> > 报告</button>
<button <button
:class="['news-tab', { active: activeTab === 'capital' }]" :class="['news-tab', { active: activeTab === 'capital' }]"
@click="$emit('update:activeTab', 'capital')" @click="$emit('update:activeTab', 'capital')"
>调查项目</button> > 项目</button>
</div> </div>
<div class="news-sort" ref="sortDropdownRef"> <div class="news-sort" ref="sortDropdownRef">
...@@ -172,9 +172,10 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside)) ...@@ -172,9 +172,10 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside))
margin-left: auto; margin-left: auto;
position: relative; position: relative;
flex-shrink: 0; flex-shrink: 0;
margin-right: 10px;
} }
.news-sort-btn { .news-sort-btn {
width: 120px; width: 150px;
height: 32px; height: 32px;
display: flex; display: flex;
align-items: center; align-items: center;
...@@ -185,6 +186,7 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside)) ...@@ -185,6 +186,7 @@ onUnmounted(() => document.removeEventListener('click', handleClickOutside))
background: rgba(255, 255, 255, 1); background: rgba(255, 255, 255, 1);
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
margin-top: 15px;
} }
.news-sort-btn:hover { .news-sort-btn:hover {
border-color: rgba(5, 95, 194, 0.4); border-color: rgba(5, 95, 194, 0.4);
......
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
<div class="news-main"> <div class="news-main">
<div class="news-grid"> <div class="news-grid">
<NewsCard <NewsCard
v-for="item in newsList" v-for="item in (activeTab === 'local' ? newsList : projectList)"
:key="item.reportId" :key="item.reportId"
:item="item" :item="item"
/> />
</div> </div>
<NewsPagination <NewsPagination
:total="totalNews" :total="activeTab === 'local' ? totalNews : totalProjects"
v-model:current-page="currentPage" v-model:current-page="currentPage"
:page-size="pageSize" :page-size="pageSize"
/> />
...@@ -41,7 +41,7 @@ import NewsSidebar from './NewsSidebar.vue' ...@@ -41,7 +41,7 @@ import NewsSidebar from './NewsSidebar.vue'
import NewsCard from './NewsCard.vue' import NewsCard from './NewsCard.vue'
import NewsPagination from './NewsPagination.vue' import NewsPagination from './NewsPagination.vue'
import { getIndustryKeyList } from '@/api/bill/billHome.js' import { getIndustryKeyList } from '@/api/bill/billHome.js'
import { getFindingsReport } from '@/api/characterPage/characterPage.js' import { getFindingsReport, getInvestigationProject } from '@/api/characterPage/characterPage.js'
const route = useRoute() const route = useRoute()
const personId = computed(() => route.params.personId || route.query.personId || '') const personId = computed(() => route.params.personId || route.query.personId || '')
...@@ -51,7 +51,9 @@ const activeTab = ref('local') ...@@ -51,7 +51,9 @@ const activeTab = ref('local')
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = 12 const pageSize = 12
const totalNews = ref(0) const totalNews = ref(0)
const totalProjects = ref(0)
const newsList = ref([]) const newsList = ref([])
const projectList = ref([])
const loading = ref(false) const loading = ref(false)
const sortBy = ref('publishTimeDesc') const sortBy = ref('publishTimeDesc')
...@@ -79,17 +81,16 @@ async function loadFilterOptions() { ...@@ -79,17 +81,16 @@ async function loadFilterOptions() {
] ]
} }
function buildParams() { function buildParams({ includeSearchText = false } = {}) {
const params = { const params = {
currentPage: currentPage.value - 1, currentPage: currentPage.value - 1,
pageSize, pageSize,
sortFun: sortBy.value === 'publishTimeAsc', sortFun: sortBy.value === 'publishTimeAsc',
industryIds: selectedDomains.value.includes('all') ? '' : JSON.stringify(selectedDomains.value),
years: selectedTimes.value.includes('all') ? '' : JSON.stringify(selectedTimes.value),
} }
if (!selectedDomains.value.includes('all')) { if (includeSearchText) {
params.industryIds = selectedDomains.value params.searchText = searchText.value
}
if (!selectedTimes.value.includes('all')) {
params.years = selectedTimes.value
} }
return params return params
} }
...@@ -97,14 +98,18 @@ function buildParams() { ...@@ -97,14 +98,18 @@ function buildParams() {
function updateSort(value) { function updateSort(value) {
sortBy.value = value sortBy.value = value
currentPage.value = 1 currentPage.value = 1
loadNews() if (activeTab.value === 'local') {
loadNews()
} else {
loadProjects()
}
} }
async function loadNews() { async function loadNews() {
if (!personId.value) return if (!personId.value) return
loading.value = true loading.value = true
try { try {
const params = buildParams() const params = buildParams({ includeSearchText: true })
const res = await getFindingsReport(personId.value, params) const res = await getFindingsReport(personId.value, params)
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
newsList.value = res.data.content || [] newsList.value = res.data.content || []
...@@ -118,6 +123,32 @@ async function loadNews() { ...@@ -118,6 +123,32 @@ async function loadNews() {
} }
} }
async function loadProjects() {
if (!personId.value) return
loading.value = true
try {
const params = buildParams()
const res = await getInvestigationProject(personId.value, params)
if (res.code === 200 && res.data) {
projectList.value = (res.data.content || []).map(item => ({
reportId: item.projectId,
title: item.name,
content: item.description,
imageUrl: item.imageUrl,
time: item.time,
sourceName: item.sourceName,
industryList: item.industryList,
}))
totalProjects.value = res.data.totalElements || 0
} else {
projectList.value = []
totalProjects.value = 0
}
} finally {
loading.value = false
}
}
function toggleDomain(id) { function toggleDomain(id) {
if (id === 'all') { if (id === 'all') {
selectedDomains.value = ['all'] selectedDomains.value = ['all']
...@@ -142,17 +173,43 @@ function toggleTime(id) { ...@@ -142,17 +173,43 @@ function toggleTime(id) {
} }
} }
watch(searchText, () => {
currentPage.value = 1
if (activeTab.value === 'local') {
loadNews()
} else {
loadProjects()
}
})
watch(activeTab, (val) => {
currentPage.value = 1
if (val === 'local') {
loadNews()
} else {
loadProjects()
}
})
watch( watch(
() => [selectedDomains.value, selectedTimes.value], () => [selectedDomains.value, selectedTimes.value],
() => { () => {
currentPage.value = 1 currentPage.value = 1
loadNews() if (activeTab.value === 'local') {
loadNews()
} else {
loadProjects()
}
}, },
{ deep: true } { deep: true }
) )
watch(currentPage, () => { watch(currentPage, () => {
loadNews() if (activeTab.value === 'local') {
loadNews()
} else {
loadProjects()
}
}) })
onMounted(async () => { onMounted(async () => {
......
...@@ -104,10 +104,17 @@ ...@@ -104,10 +104,17 @@
<div class="baseInfo-item-content">{{ characterBasicInfo.country }}</div> <div class="baseInfo-item-content">{{ characterBasicInfo.country }}</div>
</div> </div>
<div class="baseInfo-item"> <div class="baseInfo-item">
<div class="baseInfo-item">
<div class="baseInfo-item-title">教育背景:</div> <div class="baseInfo-item-title">教育背景:</div>
<div class="baseInfo-item-content long" <div class="baseInfo-item-content">
v-for="item in characterBasicInfo.educationList"> <span
{{ item.school + '--' + item.major }}<br> class="education-item"
v-for="value in characterBasicInfo.educationList"
:key="value.school"
>
{{ value.school + "(" + value.major + ")" }}
</span>
</div>
</div> </div>
</div> </div>
<div class="baseInfo-item"> <div class="baseInfo-item">
...@@ -1220,4 +1227,32 @@ const companyList = ref([ ...@@ -1220,4 +1227,32 @@ const companyList = ref([
color: rgba(170, 173, 177, 1); color: rgba(170, 173, 177, 1);
font-size: 14px; font-size: 14px;
} }
.baseInfo-item {
display: flex;
align-items: flex-start;
}
.baseInfo-item-title {
flex: 0 0 88px; /* 固定左侧宽度 */
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
white-space: nowrap; /* 左侧不换行 */
}
.baseInfo-item-content {
flex: 1;
min-width: 0;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
display: flex;
flex-wrap: wrap; /* 右侧满了再换行 */
gap: 8px 12px; /* 行列间距可调 */
}
.education-item {
white-space: nowrap; /* 每个学校+专业作为一个整体不拆行 */
}
</style> </style>
\ No newline at end of file
...@@ -39,6 +39,7 @@ const getCharacterGlobalInfoFn = async () => { ...@@ -39,6 +39,7 @@ const getCharacterGlobalInfoFn = async () => {
const res = await getCharacterGlobalInfo(params); const res = await getCharacterGlobalInfo(params);
if (res.code === 200) { if (res.code === 200) {
console.log("人物全局信息111", res); console.log("人物全局信息111", res);
document.title=res.data.name
if (res.data) { if (res.data) {
characterInfo.value = res.data; characterInfo.value = res.data;
const personType = characterInfo.value.personType; const personType = characterInfo.value.personType;
......
...@@ -15,8 +15,8 @@ ...@@ -15,8 +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" :countInfo="cooperationCountInfo" placeholder="搜索合作限制" <SearchContainer v-if="homeMainRef" placeholder="搜索合作限制" :containerRef="homeMainRef" areaName=""
:containerRef="homeMainRef" areaName="" /> style="height: fit-content" />
<!-- 最新动态 --> <!-- 最新动态 -->
<div class="newdata" id="position1"> <div class="newdata" id="position1">
...@@ -164,7 +164,7 @@ const handleToPosi = id => { ...@@ -164,7 +164,7 @@ const handleToPosi = id => {
background-size: 100% 100%; background-size: 100% 100%;
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%;
z-index: -100; z-index: -100;
top: -64px; top: -64px;
} }
...@@ -177,7 +177,7 @@ const handleToPosi = id => { ...@@ -177,7 +177,7 @@ const handleToPosi = id => {
.search { .search {
width: 960px; width: 960px;
height: 168px;
margin: 0 auto 68px auto; margin: 0 auto 68px auto;
.search-main { .search-main {
...@@ -368,12 +368,12 @@ const handleToPosi = id => { ...@@ -368,12 +368,12 @@ const handleToPosi = id => {
.reslib { .reslib {
width: 1600px; width: 1600px;
height: 1633px;
margin: 0 auto 0px auto; margin: 0 auto 0px auto;
.reslib-main { .reslib-main {
width: 1600px; width: 1600px;
height: 1565px;
margin-top: 26px; margin-top: 26px;
} }
} }
......
...@@ -105,13 +105,13 @@ ...@@ -105,13 +105,13 @@
></el-image> ></el-image>
<div v-else class="box1-bottom-content-item-imgUndefined"> <div v-else class="box1-bottom-content-item-imgUndefined">
{{ {{
(ett.entityNameZh || ett.enName)?.match( (ett.orgName || ett.orgNameZh)?.match(
/[\u4e00-\u9fa5a-zA-Z0-9]/ /[\u4e00-\u9fa5a-zA-Z0-9]/
)?.[0] )?.[0]
}} }}
</div> </div>
<div class="box1-bottom-content-item-txt"> <div class="box1-bottom-content-item-txt">
{{ ett.name || ett.entityNameZh }} {{ ett.orgName || ett.orgNameZh }}
</div> </div>
</div> </div>
</div> </div>
...@@ -1494,7 +1494,7 @@ watch( ...@@ -1494,7 +1494,7 @@ watch(
const fetchEntitiesList = async (page = 1, size = 10) => { const fetchEntitiesList = async (page = 1, size = 10) => {
try { try {
console.log("activeResourceTabItem.value.id", activeResourceTabItem.value.id); console.log("activeResourceTabItem.value.id", activeResourceTabItem.value.id);
const res = await getEntitiesList(activeResourceTabItem.value.id.join(","), page, size); const res = await getEntitiesList(activeResourceTabItem.value.id, page, size);
if (res) { if (res) {
entitiesList.value = res.content.map(item => ({ entitiesList.value = res.content.map(item => ({
...item, ...item,
......
...@@ -25,8 +25,12 @@ ...@@ -25,8 +25,12 @@
<div class="left-center"> <div class="left-center">
<AnalysisBox title="出口管制分类编码(ECCN)" :showAllBtn="false"> <AnalysisBox title="出口管制分类编码(ECCN)" :showAllBtn="false">
<div class="button-list"> <div class="button-list">
<div :class="['button', { click: item.isClick }]" @click="changeECCN(item)" v-for="(item, i) in ECCNList" <div
:key="i"> :class="['button', { click: item.isClick }]"
@click="changeECCN(item)"
v-for="(item, i) in ECCNList"
:key="i"
>
<span>{{ item.ranking }}{{ item.name }}</span> <span>{{ item.ranking }}{{ item.name }}</span>
</div> </div>
</div> </div>
...@@ -44,9 +48,17 @@ ...@@ -44,9 +48,17 @@
<AnalysisBox title="商业管制清单更新历史" :showAllBtn="false"> <AnalysisBox title="商业管制清单更新历史" :showAllBtn="false">
<template #header-btn> <template #header-btn>
<div class="filters"> <div class="filters">
<el-select v-model="selectedDomain" placeholder="Select" <el-select
style="width: 150px; height: 32px; margin-right: 16px"> v-model="selectedDomain"
<el-option v-for="item in domainOptions" :key="item.value" :label="item.label" :value="item.value" /> placeholder="Select"
style="width: 150px; height: 32px; margin-right: 16px"
>
<el-option
v-for="item in domainOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select> </el-select>
<!-- <el-checkbox v-model="onlyChina">只看涉华动态</el-checkbox> --> <!-- <el-checkbox v-model="onlyChina">只看涉华动态</el-checkbox> -->
</div> </div>
...@@ -59,15 +71,21 @@ ...@@ -59,15 +71,21 @@
</div> </div>
<div class="img-zone"> <div class="img-zone">
<img :src="item.icon || title" alt /> <img :src="item.icon || title" alt />
<div v-if="i < sanctionList.length - 1" <div
:class="['img-line', { 'img-line-last': i === sanctionList.length - 1 }]"> v-if="i < sanctionList.length - 1"
</div> :class="['img-line', { 'img-line-last': i === sanctionList.length - 1 }]"
></div>
</div> </div>
<div class="main"> <div class="main">
<div class="main-title">{{ item.name }}</div> <div class="main-title">{{ item.name }}</div>
<!-- <div class="main-title" @click="handleClick(item)">{{ item.name }}</div> --> <!-- <div class="main-title" @click="handleClick(item)">{{ item.name }}</div> -->
<el-tooltip effect="dark" :content="item.summary" popper-class="common-prompt-popper" placement="top" <el-tooltip
:show-after="500"> effect="dark"
:content="item.summary"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="main-desc">{{ item.summary }}</div> <div class="main-desc">{{ item.summary }}</div>
</el-tooltip> </el-tooltip>
<div class="tag-box"> <div class="tag-box">
...@@ -82,8 +100,14 @@ ...@@ -82,8 +100,14 @@
</div> </div>
<div class="left-footer"> <div class="left-footer">
<div class="total-count">共 {{ totalAll }} 项</div> <div class="total-count">共 {{ totalAll }} 项</div>
<el-pagination v-model:current-page="currentPageAll" :page-size="pageSizeAll" :total="totalAll" <el-pagination
layout="prev, pager, next" background @current-change="handlePageChangeAll" /> v-model:current-page="currentPageAll"
:page-size="pageSizeAll"
:total="totalAll"
layout="prev, pager, next"
background
@current-change="handlePageChangeAll"
/>
</div> </div>
</AnalysisBox> </AnalysisBox>
</div> </div>
...@@ -111,17 +135,31 @@ ...@@ -111,17 +135,31 @@
<span>关键人物</span> <span>关键人物</span>
</div> </div>
<div class="key-person-list"> <div class="key-person-list">
<div class="person-item" v-for="(item, index) in publishInfo.personList" :key="index" <div
@click="handlePerClick(item)"> class="person-item"
v-for="(item, index) in publishInfo.personList"
:key="index"
@click="handlePerClick(item)"
>
<img :src="item.imageUrl" alt /> <img :src="item.imageUrl" alt />
<div class="person-info"> <div class="person-info">
<el-tooltip effect="dark" :content="item.name" popper-class="common-prompt-popper" placement="top" <el-tooltip
:show-after="500"> effect="dark"
:content="item.name"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="name">{{ item.name }}</div> <div class="name">{{ item.name }}</div>
</el-tooltip> </el-tooltip>
<el-tooltip effect="dark" :content="item.position" popper-class="common-prompt-popper" placement="top" <el-tooltip
:show-after="500"> effect="dark"
:content="item.position"
popper-class="common-prompt-popper"
placement="top"
:show-after="500"
>
<div class="title1">{{ item.position }}</div> <div class="title1">{{ item.position }}</div>
</el-tooltip> </el-tooltip>
</div> </div>
...@@ -189,7 +227,6 @@ const handleClickOrg = item => { ...@@ -189,7 +227,6 @@ const handleClickOrg = item => {
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
// 处理点击关键人物的方法 // 处理点击关键人物的方法
...@@ -209,13 +246,12 @@ const handlePerClick = item => { ...@@ -209,13 +246,12 @@ const handlePerClick = item => {
const handleClick = item => { const handleClick = item => {
console.log("点击了实体名称:", item); console.log("点击了实体名称:", item);
const route = router.resolve({ const route = router.resolve({
path: "/exportControl/singleSanction", path: "/finance/singleSanction",
query: { query: {
id: item.id id: item.id
} }
}); });
window.open(route.href, "_blank"); window.open(route.href, "_blank");
}; };
const selectedDomain = ref(0); const selectedDomain = ref(0);
......
...@@ -376,29 +376,156 @@ const getRegionCountData = async () => { ...@@ -376,29 +376,156 @@ const getRegionCountData = async () => {
}; };
// 实体清单-数据统计- 制裁实体领域数量变化趋势 // 实体清单-数据统计- 制裁实体领域数量变化趋势
const domainNumChartOption = ref({}); const domainNumChartOption = ref({
color: [],
tooltip: {
trigger: "axis",
backgroundColor: "rgba(255, 255, 255, 0.9)",
textStyle: {
color: "#666"
},
extraCssText: "box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); border-radius: 4px;"
},
grid: {
top: "15%",
right: "2%",
bottom: "5%",
left: "2%",
containLabel: true
},
legend: {
type: "scroll",
show: true,
top: 0,
left: "center",
icon: "circle",
itemWidth: 12,
itemHeight: 12,
data: [],
textStyle: {
fontFamily: "Microsoft YaHei",
fontSize: 16,
fontWeight: 400,
lineHeight: 24,
color: "rgb(95, 101, 108)"
}
},
xAxis: [
{
type: "category",
boundaryGap: false,
data: [],
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: "#999",
fontSize: 12,
margin: 15
}
}
],
yAxis: [
{
type: "value",
min: 0,
// max: 100,
// interval: 20,
axisLine: {
show: false
},
axisTick: {
show: false
},
axisLabel: {
color: "#999",
fontSize: 12
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#E0E6F1"
}
}
}
],
series: []
});
const activeDomainTab = ref("year"); const activeDomainTab = ref("year");
const handleDomainTabChange = tab => { const handleDomainTabChange = tab => {
activeDomainTab.value = tab; activeDomainTab.value = tab;
getDomainNumData(); getDomainNumData();
}; };
// 处理领域趋势数据的方法
const processDomainTrendData = rawData => {
// 提取所有的月份作为标题
const titles = rawData.map(item => item.year).reverse();
// 收集所有不重复的领域名称
const domainNamesSet = new Set();
rawData.forEach(item => {
item.domainCountInfo.forEach(domain => {
domainNamesSet.add(domain.name);
});
});
const domainNames = Array.from(domainNamesSet);
// 定义颜色映射
const colorMap = {
人工智能: "#E34D59",
新一代通信网络: "#FF9F1C",
: "#FFB3B3",
生物科技: "#00A79D",
量子科技: "#7B61FF",
先进制造: "#363B42",
新能源: "#2BA471",
太空: "#3762F0",
集成电路: "#0052D9",
新材料: "#FFD900",
航空航天: "#3762F0",
海洋: "#76D1FF",
深海: "#002060",
其他: "#A6A6A6"
};
// 生成数据系列
const dataSeries = domainNames.map(domainName => {
const values = rawData
.map(yearData => {
const domainItem = yearData.domainCountInfo.find(d => d.name === domainName);
return domainItem ? domainItem.count : 0;
})
.reverse(); // 数据值也需要跟随标题反转顺序
return {
name: domainName,
color: colorMap[domainName] || `#${Math.floor(Math.random() * 16777215).toString(16)}`, // 如果没有预定义颜色,则随机生成
value: values
};
});
return {
title: titles,
data: dataSeries
};
};
const getDomainNumData = async () => { const getDomainNumData = async () => {
// 参数 // 参数
const param = { const param = {
IDsanTypeId: activeDomainTab.value === "year" ? "year" : "record", countType: activeDomainTab.value === "year" ? "year" : "record",
type: sanTypeId.value sanTypeId: sanTypeId.value
}; };
try { try {
const res = await getDomainNum(param); const res = await getDomainNum(param);
if (res && res.code === 200) { if (res && res.length > 0) {
domainNumChartOption.value = getMultiLineChart({ const processedData = processDomainTrendData(res);
data: res.data || [], domainNumChartOption.value = getMultiLineChart(processedData);
xAxis: res.xAxis || [], console.log("获取实体清单-数据统计-processedData:", processedData);
yAxis: res.yAxis || [], console.log("获取实体清单-数据统计-domainNumChartOption:", domainNumChartOption.value);
title: "制裁实体领域数量变化趋势",
xAxisName: "时间",
yAxisName: "数量"
});
} }
} catch (error) { } catch (error) {
console.error("获取实体清单-数据统计-制裁实体领域数量变化趋势失败:", error); console.error("获取实体清单-数据统计-制裁实体领域数量变化趋势失败:", error);
......
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论