提交 3766c4e2 authored 作者: 朱政's avatar 朱政

Merge branch 'master' into zz-dev

...@@ -10,6 +10,7 @@ lerna-debug.log* ...@@ -10,6 +10,7 @@ lerna-debug.log*
*.rar *.rar
*.zip *.zip
*.7z *.7z
*.rest
# Dependencies # Dependencies
node_modules node_modules
......
...@@ -7,32 +7,41 @@ import request from "@/api/request.js"; ...@@ -7,32 +7,41 @@ import request from "@/api/request.js";
* @param {String} params.date - 日期 * @param {String} params.date - 日期
*/ */
export function getAllUnionList(params) { export function getAllUnionList(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/unionList/${params.date}` url: `/api/union/union/unionList/${params.date}`,
}) params: {
domainId: params.domainId ? params.domainId : null
}
});
} }
// 全联盟-获取排华数量 // 全联盟-获取排华数量
/** /**
* @header token * @header token
*/ */
export function getUnionCount() { export function getUnionCount(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/unionCount` url: `/api/union/union/unionCount`,
}) params: {
currentPage: params.page ? params.page : 1,
pageSize: params.pageSize ? params.pageSize : 10,
domainId: params.domainId ? params.domainId : null
}
});
} }
// 全联盟-获取排华联盟动态 // 全联盟-获取排华联盟动态
/** /**
* @header token * @header token
*/ */
export function getDynamic() { export function getDynamic(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/dynamic` url: `/api/union/union/dynamic`,
}) params
});
} }
// 全联盟-获取排华联盟预警 // 全联盟-获取排华联盟预警
...@@ -42,10 +51,10 @@ export function getDynamic() { ...@@ -42,10 +51,10 @@ export function getDynamic() {
* @header token * @header token
*/ */
export function getPrediction() { export function getPrediction() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/prediction` url: `/api/union/union/prediction`
}) });
} }
// 全联盟-获取排华联盟领域分布 // 全联盟-获取排华联盟领域分布
...@@ -55,10 +64,10 @@ export function getPrediction() { ...@@ -55,10 +64,10 @@ export function getPrediction() {
* @header token * @header token
*/ */
export function getIndustry() { export function getIndustry() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/industry` url: `/api/union/union/industry`
}) });
} }
// 全联盟-获取排华联盟国家紧密度 // 全联盟-获取排华联盟国家紧密度
...@@ -68,8 +77,8 @@ export function getIndustry() { ...@@ -68,8 +77,8 @@ export function getIndustry() {
* @header token * @header token
*/ */
export function getCountryRelation() { export function getCountryRelation() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/union/union/countryRelation` url: `/api/union/union/countryRelation`
}) });
} }
\ No newline at end of file
...@@ -117,7 +117,7 @@ export function getBillContentId(params) { ...@@ -117,7 +117,7 @@ export function getBillContentId(params) {
// 主要条款-根据原文ID获取条款内容 // 主要条款-根据原文ID获取条款内容
/** /**
* @param {billid,id,cRelated,currentPage,pageSize} * @param {billId,id,cRelated,currentPage,pageSize,domainNameList,measuresNameList,content}
* @header token * @header token
*/ */
export function getBillContentTk(params) { export function getBillContentTk(params) {
......
...@@ -135,6 +135,26 @@ export function getBillsPerson(params, signal) { ...@@ -135,6 +135,26 @@ export function getBillsPerson(params, signal) {
}) })
} }
// 获取议员合作关系
export function getBillsPersonRel(params, signal) {
return request({
method: 'GET',
url: `/api/BillOverview/billsPersonRel`,
params,
signal
})
}
// 获取涉华委员会及其法案
export function getBillsIsCnCommittee(params, signal) {
return request({
method: 'GET',
url: `/api/BillOverview/billsIsCnCommittee`,
params,
signal
})
}
// 获取提出部门列表 // 获取提出部门列表
export function getPostOrgList() { export function getPostOrgList() {
return request({ return request({
...@@ -149,4 +169,100 @@ export function getPostMemberList() { ...@@ -149,4 +169,100 @@ export function getPostMemberList() {
method: 'GET', method: 'GET',
url: `/api/BillDict/member`, url: `/api/BillDict/member`,
}) })
} }
\ No newline at end of file
/**
* 获取筛选项配置 - 行业列表
* GET /api/billImpactAnalysis/industry/hylyList
*/
export async function getIndustryKeyList() {
return request('/api/billImpactAnalysis/industry/hylyList', {
method: 'GET',
})
}
/**
* 获取进度阶段配置
* GET /api/commonDict/bill/stage
*/
export async function getBillStageConfig() {
return request('/api/commonDict/bill/stage', {
method: 'GET',
})
}
/**
* 获取法案列表
* GET /api/personHomepage/historyBill/{personId}
* @param {string} personId - 人物ID
* @param {Object} params - 查询参数(不包含分页参数)
*/
export async function getHistoryBillList(personId, params = {}) {
const queryString = Object.entries(params)
.filter(([, value]) => value !== undefined && value !== null && value !== '')
.map(([key, value]) => {
if (Array.isArray(value)) {
return value.map(v => `${encodeURIComponent(key)}=${encodeURIComponent(v)}`).join('&')
}
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
})
.join('&')
const url = queryString
? `/api/personHomepage/historyBill/${personId}?${queryString}`
: `/api/personHomepage/historyBill/${personId}`
return request(url, {
method: 'GET',
})
}
/**
* 获取法案列表(含阶段字典)
* @param {string} personId - 人物ID
* @param {Object} params - 查询参数
*/
export async function getHistoryBillListWithStage(personId, params = {}) {
const stageRes = await getBillStageConfig()
const billRes = await getHistoryBillList(personId, params)
return {
stageConfig: stageRes,
billList: billRes,
}
}
export function getSortOptions() {
return Promise.resolve({
code: 200,
data: [
{ value: 'latestMotionTimeDesc', label: '最新修议时间倒序' },
{ value: 'latestMotionTimeAsc', label: '最新修议时间正序' },
],
})
}
export async function getPotentialNewsList(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',
})
}
export async function getPotentialNewsKeywords(personId) {
return request(`/api/personHomepage/historyBill/clauseKeyword/${personId}`, {
method: 'GET',
})
}
\ No newline at end of file
...@@ -98,8 +98,8 @@ export function getCharacterResume(params) { ...@@ -98,8 +98,8 @@ export function getCharacterResume(params) {
*/ */
export function getCharacterView(params) { export function getCharacterView(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/personHomepage/option/${params.personId}/${params.year}`, url: `/api/personHomepage/option/`,
params, params,
}) })
} }
...@@ -112,7 +112,7 @@ export function getCharacterView(params) { ...@@ -112,7 +112,7 @@ export function getCharacterView(params) {
export function getCharacterFundSource(params) { export function getCharacterFundSource(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/personHomepage/personFunds/${params.personId}/${params.year}`, url: `/api/personHomepage/personFunds/`,
params, params,
}) })
} }
...@@ -141,4 +141,62 @@ export function getCharacterRelatedEntity(params) { ...@@ -141,4 +141,62 @@ export function getCharacterRelatedEntity(params) {
url: `/api/personHomepage/personRelation/${params.personId}/${params.startTime}`, url: `/api/personHomepage/personRelation/${params.personId}/${params.startTime}`,
params, params,
}) })
}
// 获取人物教育履历
/**
* @param {personId}
* @header token
*/
export function getCharacterReducationResume(params) {
return request({
method: 'GET',
url: `/api/personHomepage/educationResume/${params.personId}`,
params,
})
}
export async function getFindingsReport(personId, params = {}) {
const queryParts = []
Object.entries(params).forEach(([key, value]) => {
if (value === undefined || value === null || value === '') return
if (Array.isArray(value)) {
value.forEach(v => {
queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`)
})
} else {
queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
}
})
const queryString = queryParts.join('&')
const url = queryString
? `/api/personHomepage/findingsReport/${personId}?${queryString}`
: `/api/personHomepage/findingsReport/${personId}`
return request(url, { method: 'GET' })
}
/**
* 获取创新主体列表(大学/实验室/企业)
* @param {Object} params - 请求参数
* @param {string} params.arealist - 科技领域ID
* @param {number} params.currentPage - 当前页码(从0开始)
* @param {string} params.keywords - 搜索关键词
* @param {number} params.pageSize - 每页条数
* @param {string} params.subjectTypeId - 主体类型ID
* @returns {Promise} 返回列表数据
*/
export async function getSubjectList(params) {
return request('/api/innovateSubject/findListBySubjectTypeId', {
method: 'GET',
params
})
}
export function getareaType(params) {
return request({
method: 'GET',
url: `/api/commonDict/areaType`,
params
})
} }
\ No newline at end of file
...@@ -130,3 +130,14 @@ export function getBillPersonPoliContribution(params) { ...@@ -130,3 +130,14 @@ export function getBillPersonPoliContribution(params) {
url: `/api/billDeepDive/processAnalyze/xj/${params.id}/${params.personId}` url: `/api/billDeepDive/processAnalyze/xj/${params.id}/${params.personId}`
}) })
} }
// 根据法案ID和人物ID获取政治献金领域分布
/**
* @param {id, personId}
*/
export function getBillPersonPoliDomain(params) {
return request({
method: 'GET',
url: `/api/billDeepDive/processAnalyze/xjDomain/${params.id}/${params.personId}`
})
}
import request from "@/api/request.js";
const INTELLECTUAL_API = "/intelligent-api";
export class IntelligentResultWrapper<T> {
result: T;
status: string;
}
export class TextEntity {
text_span: string;
type: string;
}
// 智能化:提取文本实体
export function extractTextEntity(text: string): Promise<IntelligentResultWrapper<TextEntity[]>> {
return request({
url: `${INTELLECTUAL_API}/extract-entity`,
method: "POST",
data: {
text
}
});
}
...@@ -2,10 +2,11 @@ import request from "@/api/request.js"; ...@@ -2,10 +2,11 @@ import request from "@/api/request.js";
// 概览页-------------------------------------------------------------------- // 概览页--------------------------------------------------------------------
// 概览统计 // 概览统计
export function getStatCount() { export function getStatCount(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/marketsearchHome/statCount` url: `/api/marketsearchHome/statCount`,
params
}) })
} }
......
...@@ -35,7 +35,7 @@ export function getHotNews() { ...@@ -35,7 +35,7 @@ export function getHotNews() {
export function getHotNewsByArea(params) { export function getHotNewsByArea(params) {
return request({ return request({
method: 'GET', method: 'GET',
url: `/api/news/hotNews`, url: `/api/news/hotAreaNews/${params.moduleId}`,
params params
}) })
} }
...@@ -54,4 +54,11 @@ export function getAreaList() { ...@@ -54,4 +54,11 @@ export function getAreaList() {
method: 'GET', method: 'GET',
url: `/api/commonDict/areaType`, url: `/api/commonDict/areaType`,
}) })
} }
\ No newline at end of file export function getNewsDetail(params) {
return request({
method: 'GET',
url: `/api/news/findById/${params.newsId}`,
params
})
}
...@@ -72,6 +72,15 @@ service.interceptors.response.use( ...@@ -72,6 +72,15 @@ service.interceptors.response.use(
}, },
error => { error => {
console.log('err' + error) console.log('err' + error)
const isCanceledError =
error?.code === 'ERR_CANCELED' ||
error?.name === 'CanceledError' ||
error?.name === 'AbortError' ||
(typeof error?.message === 'string' && /canceled/i.test(error.message))
// 重复请求触发的取消不提示错误
if (isCanceledError) return Promise.reject(error)
// 处理token过期或无效的情况 // 处理token过期或无效的情况
if (error.response && (error.response.status === 401 || error.response.status === 403)) { if (error.response && (error.response.status === 401 || error.response.status === 403)) {
......
...@@ -2,74 +2,99 @@ import request from "@/api/request.js"; ...@@ -2,74 +2,99 @@ import request from "@/api/request.js";
// 全要素统计 // 全要素统计
export function getElementCount(params) { export function getElementCount(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/elementCount/${params.date}`, url: `/api/element/elementCount/${params.date}`
}) });
}
// 美对华科技要素打压遏制数量趋势
export function getElementSuppressTrend(params) {
return request({
method: "GET",
url: `/api/element/DomainContainmentTrend/${params.date}`,
params: {
domainId: params.domainId ? params.domainId : null
}
});
} }
// 最新动态 // 最新动态
export function getNewDynamics() { export function getNewDynamics() {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/newDynamics`, url: `/api/element/newDynamics`
}) });
} }
// 美对我要素打压情况 // 美对我要素打压情况
/** /**
* @param {currentPage, pageSize} * @param {currentPage, pageSize}
*/ */
export function getElementSuppress(params) { export function getElementSuppress(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/elementSuppress/${params.date}`, url: `/api/element/elementSuppress/${params.date}`,
params params: {
}) elementId: params.elementId ? params.elementId : null,
page: params.currentPage,
pageSize: params.pageSize
}
});
} }
// 关键词云-上 // 关键词云-上
/** /**
* @param {date} * @param {date}
*/ */
export function getKeyWordUp(params) { export function getKeyWordUp(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/getKeyWordUp/${params.date}`, url: `/api/element/getKeyWordUp/${params.date}`,
}) params: {
elementId: params.elementId ? params.elementId : null
}
});
} }
// 美自身要素发展情况 // 美自身要素发展情况
/** /**
* @param {currentPage, pageSize} * @param {currentPage, pageSize}
*/ */
export function getElementDevelop(params) { export function getElementDevelop(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/elementDevelop/${params.date}`, url: `/api/element/elementDevelop/${params.date}`,
params params: {
}) elementId: params.elementId ? params.elementId : null,
page: params.currentPage,
pageSize: params.pageSize
}
});
} }
// 关键词云-下 // 关键词云-下
/** /**
* @param {currentPage, pageSize} * @param {currentPage, pageSize}
*/ */
export function getKeyWordDown(params) { export function getKeyWordDown(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/getKeyWordDown/${params.date}`, url: `/api/element/getKeyWordDown/${params.date}`,
}) params: {
elementId: params.elementId ? params.elementId : null
}
});
} }
// 通过id获取政令详细信息 // 通过id获取政令详细信息
/** /**
* @param {id} * @param {id}
*/ */
export function getOrderInfo(params) { export function getOrderInfo(params) {
return request({ return request({
method: 'GET', method: "GET",
url: `/api/element/getOrderInfo/${params.id}`, url: `/api/element/getOrderInfo/${params.id}`,
params params
}) });
} }
\ No newline at end of file
export const countryCoordMap = {
// 亚洲
中国: [104.1954, 35.8617], // 中国地理中心
日本: [138.2529, 36.2048], // 日本地理中心
韩国: [127.7669, 35.9078], // 韩国首尔
朝鲜: [127.5101, 40.3399], // 朝鲜平壤
蒙古: [103.8198, 46.8625], // 蒙古乌兰巴托
越南: [108.2772, 14.0583], // 越南河内
泰国: [100.9925, 15.87], // 泰国曼谷
马来西亚: [101.9758, 4.2105], // 马来西亚吉隆坡
新加坡: [103.8198, 1.3521], // 新加坡
印度尼西亚: [113.9213, -0.7893], // 印尼雅加达
菲律宾: [121.774, 12.8797], // 菲律宾马尼拉
印度: [78.9629, 20.5937], // 印度新德里
巴基斯坦: [73.0479, 30.3753], // 巴基斯坦伊斯兰堡
孟加拉国: [90.3563, 23.685], // 孟加拉达卡
斯里兰卡: [80.7718, 7.8731], // 斯里兰卡科伦坡
尼泊尔: [85.3131, 27.7172], // 尼泊尔加德满都
不丹: [89.412, 27.5142], // 不丹廷布
缅甸: [96.1997, 21.9162], // 缅甸内比都
柬埔寨: [104.9162, 11.5621], // 柬埔寨金边
老挝: [102.4955, 19.8563], // 老挝万象
文莱: [114.9409, 4.5353], // 文莱斯里巴加湾
东帝汶: [125.573, -8.8742], // 东帝汶帝力
阿富汗: [69.1761, 33.9391], // 阿富汗喀布尔
伊拉克: [44.3661, 33.2232], // 伊拉克巴格达
伊朗: [53.688, 32.4279], // 伊朗德黑兰
沙特阿拉伯: [45.0792, 23.8859], // 沙特利雅得
阿联酋: [54.3705, 23.4241], // 阿联酋阿布扎比
阿曼: [57.595, 23.588], // 阿曼马斯喀特
科威特: [47.999, 29.3117], // 科威特科威特城
卡塔尔: [51.531, 25.2854], // 卡塔尔多哈
巴林: [50.5853, 26.0275], // 巴林麦纳麦
也门: [48.5164, 15.5527], // 也门萨那
约旦: [36.2384, 30.5852], // 约旦安曼
黎巴嫩: [35.5018, 33.8938], // 黎巴嫩贝鲁特
以色列: [35.2137, 31.0461], // 以色列耶路撒冷
巴勒斯坦: [35.2037, 31.9522], // 巴勒斯坦拉姆安拉
叙利亚: [36.8065, 33.5138], // 叙利亚大马士革
土耳其: [35.2433, 38.9637], // 土耳其安卡拉
塞浦路斯: [33.238, 35.1264], // 塞浦路斯尼科西亚
哈萨克斯坦: [66.9237, 48.0196], // 哈萨克斯坦努尔苏丹
吉尔吉斯斯坦: [74.5818, 42.8746], // 吉尔吉斯斯坦比什凯克
塔吉克斯坦: [68.7872, 38.5598], // 塔吉克斯坦杜尚别
乌兹别克斯坦: [69.2401, 41.3775], // 乌兹别克斯坦塔什干
土库曼斯坦: [59.5568, 38.9697], // 土库曼斯坦阿什哈巴德
阿塞拜疆: [47.5769, 40.1431], // 阿塞拜疆巴库
格鲁吉亚: [44.8337, 41.7151], // 格鲁吉亚第比利斯
亚美尼亚: [44.5136, 40.0691], // 亚美尼亚埃里温
// 欧洲
俄罗斯: [37.6184, 55.7558], // 俄罗斯莫斯科
德国: [10.4515, 51.1657], // 德国柏林
英国: [-3.436, 55.3781], // 英国伦敦
法国: [2.2137, 46.2276], // 法国巴黎
意大利: [12.5674, 41.8719], // 意大利罗马
西班牙: [-3.7492, 40.4637], // 西班牙马德里
葡萄牙: [-8.2275, 39.3999], // 葡萄牙里斯本
荷兰: [5.2913, 52.1326], // 荷兰阿姆斯特丹
比利时: [4.3517, 50.8503], // 比利时布鲁塞尔
卢森堡: [6.1296, 49.8153], // 卢森堡卢森堡市
瑞士: [8.2275, 46.8182], // 瑞士伯尔尼
奥地利: [16.3738, 48.2082], // 奥地利维也纳
爱尔兰: [-8.2439, 53.1424], // 爱尔兰都柏林
芬兰: [25.7482, 61.9241], // 芬兰赫尔辛基
瑞典: [18.6435, 60.1282], // 瑞典斯德哥尔摩
挪威: [10.7461, 60.472], // 挪威奥斯陆
丹麦: [9.5018, 56.2639], // 丹麦哥本哈根
冰岛: [-19.0208, 64.9631], // 冰岛雷克雅未克
波兰: [19.1451, 51.9194], // 波兰华沙
捷克: [14.4208, 50.0755], // 捷克布拉格
斯洛伐克: [19.699, 48.1486], // 斯洛伐克布拉迪斯拉发
匈牙利: [19.0402, 47.4979], // 匈牙利布达佩斯
罗马尼亚: [24.9375, 45.9432], // 罗马尼亚布加勒斯特
保加利亚: [23.3219, 42.7339], // 保加利亚索非亚
塞尔维亚: [20.4651, 44.804], // 塞尔维亚贝尔格莱德
克罗地亚: [16.0, 45.1], // 克罗地亚萨格勒布
斯洛文尼亚: [14.5146, 46.0569], // 斯洛文尼亚卢布尔雅那
波黑: [18.3564, 43.8486], // 波黑萨拉热窝
黑山: [19.2664, 42.4304], // 黑山波德戈里察
马其顿: [21.7453, 41.6086], // 北马其顿斯科普里
阿尔巴尼亚: [20.1683, 41.3275], // 阿尔巴尼亚地拉那
希腊: [21.8243, 39.0742], // 希腊雅典
乌克兰: [30.5234, 50.4501], // 乌克兰基辅
白俄罗斯: [27.5615, 53.9045], // 白俄罗斯明斯克
摩尔多瓦: [28.8613, 47.4116], // 摩尔多瓦基希讷乌
立陶宛: [25.2797, 54.6872], // 立陶宛维尔纽斯
拉脱维亚: [24.1052, 56.9496], // 拉脱维亚里加
爱沙尼亚: [24.7536, 59.437], // 爱沙尼亚塔林
马耳他: [14.5146, 35.8997], // 马耳他瓦莱塔
列支敦士登: [9.5215, 47.166], // 列支敦士登瓦杜兹
摩纳哥: [7.4215, 43.7384], // 摩纳哥摩纳哥城
安道尔: [1.5218, 42.5063], // 安道尔安道尔城
圣马力诺: [12.4417, 43.9424], // 圣马力诺圣马力诺市
梵蒂冈: [12.4534, 41.9028], // 梵蒂冈梵蒂冈城
// 非洲
埃及: [30.8025, 26.8206], // 埃及开罗
南非: [25.0, -30.0], // 南非比勒陀利亚
尼日利亚: [8.6753, 9.082], // 尼日利亚阿布贾
肯尼亚: [36.8219, -1.2921], // 肯尼亚内罗毕
埃塞俄比亚: [38.7469, 9.145], // 埃塞俄比亚亚的斯亚贝巴
坦桑尼亚: [34.8888, -6.369], // 坦桑尼亚达累斯萨拉姆
加纳: [-0.187, 5.6037], // 加纳阿克拉
科特迪瓦: [-5.581, 7.5399], // 科特迪瓦亚穆苏克罗
喀麦隆: [12.3578, 4.0417], // 喀麦隆雅温得
塞内加尔: [-17.4441, 14.6937], // 塞内加尔达喀尔
马里: [-7.6009, 17.5707], // 马里巴马科
布基纳法索: [-1.5332, 12.2383], // 布基纳法索瓦加杜古
尼日尔: [8.9676, 13.518], // 尼日尔尼亚美
乍得: [19.3973, 15.4542], // 乍得恩贾梅纳
苏丹: [32.535, 15.588], // 苏丹喀土穆
南苏丹: [31.6, 6.87], // 南苏丹朱巴
摩洛哥: [-7.6114, 31.7917], // 摩洛哥拉巴特
阿尔及利亚: [2.8235, 28.0339], // 阿尔及利亚阿尔及尔
突尼斯: [10.1815, 36.8065], // 突尼斯突尼斯市
利比亚: [17.2283, 26.3351], // 利比亚的黎波里
毛里塔尼亚: [-10.9408, 20.9373], // 毛里塔尼亚努瓦克肖特
塞拉利昂: [-13.2356, 8.4606], // 塞拉利昂弗里敦
利比里亚: [-9.5018, 6.4281], // 利比里亚蒙罗维亚
几内亚: [-9.6966, 9.9456], // 几内亚科纳克里
几内亚比绍: [-15.1804, 11.8037], // 几内亚比绍比绍
冈比亚: [-16.579, 13.4432], // 冈比亚班珠尔
贝宁: [2.3158, 6.3697], // 贝宁波多诺伏
多哥: [1.2226, 6.826], // 多哥洛美
加蓬: [11.601, 0.417], // 加蓬利伯维尔
"刚果(布)": [15.2829, -4.2634], // 刚果布拉柴维尔
"刚果(金)": [15.309, -4.4419], // 刚果金金沙萨
乌干达: [32.5825, 0.3476], // 乌干达坎帕拉
卢旺达: [30.058, -1.9403], // 卢旺达基加利
布隆迪: [29.3694, -3.3731], // 布隆迪布琼布拉
马拉维: [34.9, -13.5], // 马拉维利隆圭
赞比亚: [28.2871, -15.4067], // 赞比亚卢萨卡
津巴布韦: [30.1502, -17.8252], // 津巴布韦哈拉雷
博茨瓦纳: [24.6849, -24.6282], // 博茨瓦纳哈博罗内
纳米比亚: [18.5449, -22.5594], // 纳米比亚温得和克
莱索托: [28.2336, -29.61], // 莱索托马塞卢
斯威士兰: [31.1378, -26.5225], // 斯威士兰姆巴巴内
莫桑比克: [32.5709, -25.9655], // 莫桑比克马普托
马达加斯加: [46.8691, -18.7669], // 马达加斯加塔那那利佛
毛里求斯: [57.5522, -20.3484], // 毛里求斯路易港
科摩罗: [43.865, -11.8678], // 科摩罗莫罗尼
塞舌尔: [55.4919, -4.6737], // 塞舌尔维多利亚
佛得角: [-23.5087, 16.0], // 佛得角普拉亚
圣多美和普林西比: [6.6131, 0.3385], // 圣多美和普林西比圣多美
厄立特里亚: [38.9208, 15.1794], // 厄立特里亚阿斯马拉
吉布提: [43.145, 11.8251], // 吉布提吉布提市
索马里: [45.3438, 5.1521], // 索马里摩加迪沙
中非: [20.9394, 6.6111], // 中非班吉
赤道几内亚: [8.7815, 1.6508], // 赤道几内亚马拉博
"加那利群岛(西)": [-15.5974, 28.0966], // 加那利群岛拉斯帕尔马斯
西撒哈拉: [-12.8858, 24.2155], // 西撒哈拉阿尤恩
// 北美洲
美国: [-95.7129, 37.0902], // 美国华盛顿
加拿大: [-106.3468, 56.1304], // 加拿大渥太华
墨西哥: [-102.5528, 23.6345], // 墨西哥墨西哥城
危地马拉: [-90.5578, 15.7835], // 危地马拉危地马拉城
伯利兹: [-88.2075, 17.1899], // 伯利兹贝尔莫潘
萨尔瓦多: [-88.8965, 13.7942], // 萨尔瓦多圣萨尔瓦多
洪都拉斯: [-86.2419, 15.199], // 洪都拉斯特古西加尔巴
尼加拉瓜: [-85.2072, 12.8654], // 尼加拉瓜马那瓜
哥斯达黎加: [-84.0875, 9.7489], // 哥斯达黎加圣何塞
巴拿马: [-79.5199, 8.538], // 巴拿马巴拿马城
古巴: [-77.7812, 21.5218], // 古巴哈瓦那
海地: [-72.337, 18.9712], // 海地太子港
多米尼加: [-70.1627, 18.7357], // 多米尼加圣多明各
牙买加: [-76.802, 18.1096], // 牙买加金斯顿
巴哈马: [-77.34, 25.0343], // 巴哈马拿骚
巴巴多斯: [-59.5236, 13.1939], // 巴巴多斯布里奇顿
特立尼达和多巴哥: [-61.2225, 10.6918], // 特多西班牙港
格林纳达: [-61.6781, 12.1165], // 格林纳达圣乔治
圣卢西亚: [-60.9915, 13.9094], // 圣卢西亚卡斯特里
圣文森特和格林纳丁斯: [-61.2273, 13.2529], // 圣文森特金斯敦
安提瓜和巴布达: [-61.7964, 17.0608], // 安提瓜圣约翰
多米尼克: [-61.3709, 15.414], // 多米尼克罗索
圣基茨和尼维斯: [-62.7177, 17.3578], // 圣基茨巴斯特尔
"波多黎各(美)": [-66.5901, 18.2208], // 波多黎各圣胡安
"开曼群岛(英)": [-81.25, 19.2975], // 开曼群岛乔治敦
"百慕大(英)": [-64.8457, 32.3078], // 百慕大汉密尔顿
// 南美洲
巴西: [-51.9253, -14.235], // 巴西巴西利亚
阿根廷: [-68.3064, -38.4161], // 阿根廷布宜诺斯艾利斯
智利: [-71.5429, -35.6751], // 智利圣地亚哥
秘鲁: [-75.0152, -9.1899], // 秘鲁利马
哥伦比亚: [-74.0818, 4.711], // 哥伦比亚波哥大
委内瑞拉: [-66.9036, 6.4238], // 委内瑞拉加拉加斯
厄瓜多尔: [-78.5249, -0.1807], // 厄瓜多尔基多
玻利维亚: [-68.15, -16.5], // 玻利维亚苏克雷
巴拉圭: [-58.4434, -25.2637], // 巴拉圭亚松森
乌拉圭: [-56.1645, -34.9011], // 乌拉圭蒙得维的亚
圭亚那: [-58.1551, 6.8013], // 圭亚那乔治敦
苏里南: [-55.2038, 3.9193], // 苏里南帕拉马里博
法属圭亚那: [-52.3264, 4.9375], // 法属圭亚那卡宴
// 大洋洲
澳大利亚: [133.7751, -25.2744], // 澳大利亚堪培拉
新西兰: [174.886, -40.9006], // 新西兰惠灵顿
巴布亚新几内亚: [147.1557, -6.3149], // 巴新莫尔兹比港
所罗门群岛: [159.95, -9.6457], // 所罗门群岛霍尼亚拉
瓦努阿图: [167.6864, -16.5225], // 瓦努阿图维拉港
斐济: [179.4144, -18.1416], // 斐济苏瓦
萨摩亚: [-172.1626, -13.759], // 萨摩亚阿皮亚
汤加: [-175.1982, -21.1789], // 汤加努库阿洛法
基里巴斯: [173.038, 1.2921], // 基里巴斯塔拉瓦
密克罗尼西亚: [158.166, 6.9167], // 密克罗尼西亚帕利基尔
马绍尔群岛: [171.8485, 7.1315], // 马绍尔群岛马朱罗
帕劳: [134.5825, 7.5149], // 帕劳梅莱凯奥克
瑙鲁: [166.9315, -0.5228], // 瑙鲁亚伦
图瓦卢: [179.1942, -8.5243], // 图瓦卢富纳富提
"库克群岛(新)": [-159.7756, -21.2367], // 库克群岛阿瓦鲁阿
"纽埃(新)": [-169.8672, -19.0544], // 纽埃阿洛菲
"托克劳(新)": [-171.7415, -8.9672], // 托克劳法考福
美属萨摩亚: [-170.6991, -14.271], // 美属萨摩亚马努阿
"关岛(美)": [144.7937, 13.4443], // 关岛阿加尼亚
"北马里亚纳群岛(美)": [145.6739, 15.1178], // 北马里亚纳塞班
法属波利尼西亚: [-149.5986, -17.6797], // 法属波利尼西亚帕皮提
"新喀里多尼亚(法)": [166.4572, -21.5547] // 新喀里多尼亚努美阿
};
...@@ -103,6 +103,7 @@ const headerTitleClasses = computed(() => [ ...@@ -103,6 +103,7 @@ const headerTitleClasses = computed(() => [
border-bottom: 1px solid #ebeef5; border-bottom: 1px solid #ebeef5;
/* background-color: #f8fafc; */ /* background-color: #f8fafc; */
padding-left: 0; padding-left: 0;
// background: linear-gradient(180deg, rgb(231, 243, 255) 0%, rgba(231, 243, 255, 0) 100%);
} }
.header-left { .header-left {
...@@ -132,10 +133,10 @@ const headerTitleClasses = computed(() => [ ...@@ -132,10 +133,10 @@ const headerTitleClasses = computed(() => [
} }
.header-icon { .header-icon {
width: 20px; width: 22px;
height: 20px; height: 18px;
margin-left: 5px; margin-left: 5px;
margin-right: 19px; margin-right: 14px;
} }
.blue-title-block { .blue-title-block {
...@@ -148,14 +149,13 @@ const headerTitleClasses = computed(() => [ ...@@ -148,14 +149,13 @@ const headerTitleClasses = computed(() => [
.header-title { .header-title {
font-family: $base-font-family; font-family: $base-font-family;
font-size: $base-font-size; font-size: 20px;
font-weight: 700; font-weight: 700;
/* color: var(--color-main-active); */ /* color: var(--color-main-active); */
/* color: var(--base-color); */ /* color: var(--base-color); */
color: $base-color; color: $base-color;
line-height: 48px; line-height: 48px;
padding: 0 12px; // padding: 0 12px;
font-size: 20px;
} }
.header-title-primary { .header-title-primary {
......
const getGraphChart = (nodes, links, layoutType) => {
const option = {
// title: {
// text: '企业关系网络',
// subtext: '节点图标表示企业,箭头表示关联方向',
// top: 'top',
// left: 'center',
// textStyle: {
// fontSize: 20,
// color: '#2c3e50'
// }
// },
// tooltip: {
// formatter: function (params) {
// if (params.dataType === 'node') {
// return `<div style="font-weight:bold;margin-bottom:5px">${params.data.name}</div>
// <div>类别: ${categories[params.data.category].name}</div>
// <div>关联度: ${params.data.value}</div>`;
// } else {
// return `<div>${nodes[params.data.source].name} → ${nodes[params.data.target].name}</div>
// <div>关联强度: ${params.data.value}</div>`;
// }
// }
// },
legend: {
// data: categories.map(c => c.name),
show: false,
top: 40,
textStyle: {
fontSize: 12
}
},
animation: true,
animationDuration: 1000,
animationEasing: 'cubicOut',
series: [{
type: 'graph',
itemStyle: {
color: '#73C0DE'
},
layout: layoutType,
data: nodes,
links: links,
// categories: categories,
roam: true,
label: {
show: true,
position: 'bottom',
formatter: '{b}',
fontSize: 12,
fontWeight: 'bold',
// backgroundColor: 'rgba(255,255,255,0.8)',
padding: [4, 6],
borderRadius: 4
},
lineStyle: {
color: 'source',
curveness: 0,
width: 2,
type: 'dashed',
color: '#AED6FF'
},
edgeSymbol: ['none', 'arrow'],
edgeSymbolSize: [0, 10],
emphasis: {
focus: 'adjacency',
lineStyle: {
width: 4
},
label: {
show: true,
fontSize: 14
}
},
force: {
repulsion: 300,
gravity: 0,
edgeLength: 300
},
edgeLabel: {
show: true,
position: 'middle',
fontSize: 14,
color: '#333',
backgroundColor: 'rgba(255,255,255,0.9)',
borderColor: '#bbb',
borderWidth: 1,
borderRadius: 4,
padding: [4, 8],
formatter: params => params.data.label ? params.data.label.formatter : ''
}
// edgeLabel: {
// show: true,
// position: 'middle',
// // fontSize: 14,
// // backgroundColor: 'rgba(255,255,255,0.9)',
// // borderColor: '#bbb',
// borderWidth: 1,
// borderRadius: 4,
// padding: [4, 8],
// // 核心:使用 formatter 函数
// formatter: function (params) {
// console.log('完整 params:', params);
// console.log('label 数据:', params.data.label);
// // 获取标签的文本内容,它存储在 params.data.label.formatter 里
// const labelText = params.data.label?.formatter || '';
// // 定义一个颜色映射
// const colorMap = {
// '合作': '#52c41a', // 绿色
// '持股': '#faad14', // 橙色
// '从属': '#f5222d' // 红色
// };
// // 根据文本内容获取对应的颜色,如果没有定义则使用默认的灰色
// const color = colorMap[labelText] || '#666';
// // 返回一个带内联样式的 HTML 字符串
// return `<span style="color: ${color};">${labelText}</span>`;
// }
// }
}],
// color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de']
};
return option
}
export default getGraphChart
\ No newline at end of file
<template>
<div class="graph-chart-wrapper" id="graph">
</div>
</template>
<script setup>
import { onMounted, nextTick } from 'vue';
import setChart from '@/utils/setChart';
import getGraphChart from './graphChart';
const props = defineProps({
nodes: {
type: Array,
default: []
},
links: {
type: Array,
default: []
},
layoutType: {
type: String,
default: 'force'
},
width: {
type: String,
default: 'force'
},
height: {
type: String,
default: 'force'
}
})
onMounted(() => {
const graph = getGraphChart(props.nodes, props.links, props.layoutType)
setChart(graph, 'graph')
})
</script>
<style lang="scss" scoped>
.graph-chart-wrapper {
width: 100%;
height: 100%;
}
</style>
\ No newline at end of file
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
<div class="sider-tabs-wrapper"> <div class="sider-tabs-wrapper">
<div class="sider-item" <div class="sider-item"
:class="{'sider-item-active': sider.active}" :class="{'sider-item-active': sider.active}"
v-for="sider, index in siderList" :key="index" v-for="(sider, index) in siderList" :key="sider.name || index"
@click="handleClickSiderItem(sider)" @click="handleClickSiderItem(sider)"
> >
<div <div
class="sider-item-text text-primary-65-clor text-tip-1" class="sider-item-text text-primary-65-clor text-tip-1"
:class="{'sider-item-text-active': sider.active}">{{ sider.name }}</div> :class="{'sider-item-text-active': sider.active}">{{ sider.name }}</div>
<div class="sider-item-icon" v-show="sider.active"> <div class="sider-item-icon" v-show="sider.active">
<img src="./active-icon.svg" alt=""> <img src="./active-icon.svg" alt="" />
</div> </div>
</div> </div>
</div> </div>
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
const props = defineProps({ const props = defineProps({
siderList: { siderList: {
type: Array, type: Array,
default: [ default: () => [
{ {
name: '分析内容1', name: '分析内容1',
active: true active: true
......
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 767" width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" />
<g id="组合 1059">
<path id="矩形 350" d="M2.50019 7.57031L12.4998 7.57048C12.7759 7.57048 12.9992 7.79409 12.9995 8.07012L13 8.07012C13.0001 8.34632 12.776 8.57031 12.4998 8.57031L2.50018 8.57015C2.22419 8.57015 2.00069 8.34649 2.00054 8.07051L2 8.07051C1.9997 7.79434 2.22403 7.57031 2.50019 7.57031Z" fill="rgb(206,79,81)" fill-rule="evenodd" />
<path id="矢量 610" d="M8 3L13 8L8 13" stroke="rgb(206,79,81)" stroke-linecap="round" stroke-width="1.000000" />
</g>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 767" width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" />
<g id="组合 1059">
<path id="矩形 350" d="M2.50019 7.57031L12.4998 7.57048C12.7759 7.57048 12.9992 7.79409 12.9995 8.07012L13 8.07012C13.0001 8.34632 12.776 8.57031 12.4998 8.57031L2.50018 8.57015C2.22419 8.57015 2.00069 8.34649 2.00054 8.07051L2 8.07051C1.9997 7.79434 2.22403 7.57031 2.50019 7.57031Z" fill="rgb(255, 149, 77)" fill-rule="evenodd" />
<path id="矢量 610" d="M8 3L13 8L8 13" stroke="rgb(255, 149, 77)" stroke-linecap="round" stroke-width="1.000000" />
</g>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 767" width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" />
<g id="组合 1059">
<path id="矩形 350" d="M2.50019 7.57031L12.4998 7.57048C12.7759 7.57048 12.9992 7.79409 12.9995 8.07012L13 8.07012C13.0001 8.34632 12.776 8.57031 12.4998 8.57031L2.50018 8.57015C2.22419 8.57015 2.00069 8.34649 2.00054 8.07051L2 8.07051C1.9997 7.79434 2.22403 7.57031 2.50019 7.57031Z" fill="rgb(232, 189, 11)" fill-rule="evenodd" />
<path id="矢量 610" d="M8 3L13 8L8 13" stroke="rgb(232, 189, 11)" stroke-linecap="round" stroke-width="1.000000" />
</g>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 767" width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" />
<g id="组合 1059">
<path id="矩形 350" d="M2.50019 7.57031L12.4998 7.57048C12.7759 7.57048 12.9992 7.79409 12.9995 8.07012L13 8.07012C13.0001 8.34632 12.776 8.57031 12.4998 8.57031L2.50018 8.57015C2.22419 8.57015 2.00069 8.34649 2.00054 8.07051L2 8.07051C1.9997 7.79434 2.22403 7.57031 2.50019 7.57031Z" fill="rgb(33, 129, 57)" fill-rule="evenodd" />
<path id="矢量 610" d="M8 3L13 8L8 13" stroke="rgb(33, 129, 57)" stroke-linecap="round" stroke-width="1.000000" />
</g>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="url(#clipPath_0)">
<defs>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="容器 767" width="16.000000" height="16.000000" x="0.000000" y="0.000000" rx="8.000000" />
<g id="组合 1059">
<path id="矩形 350" d="M2.50019 7.57031L12.4998 7.57048C12.7759 7.57048 12.9992 7.79409 12.9995 8.07012L13 8.07012C13.0001 8.34632 12.776 8.57031 12.4998 8.57031L2.50018 8.57015C2.22419 8.57015 2.00069 8.34649 2.00054 8.07051L2 8.07051C1.9997 7.79434 2.22403 7.57031 2.50019 7.57031Z" fill="rgb(5, 95, 194)" fill-rule="evenodd" />
<path id="矢量 610" d="M8 3L13 8L8 13" stroke="rgb(5, 95, 194)" stroke-linecap="round" stroke-width="1.000000" />
</g>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16.000000" height="16.000000" fill="none" customFrame="#000000">
<rect id="容器 1922" width="16.000000" height="16.000000" x="0.000000" y="0.000000" />
<path id="减去顶层" d="M15.8612 13.4253L8.86371 1.38047C8.47821 0.716925 7.51986 0.716925 7.13436 1.38047L0.136829 13.4253C-0.250475 14.0919 0.230498 14.9276 1.0015 14.9276L14.9966 14.9276C15.7676 14.9276 16.2485 14.0919 15.8612 13.4253ZM8.73076 9.86062C8.71726 10.2202 8.42184 10.5047 8.06201 10.5047C7.7017 10.5047 7.40606 10.2195 7.39322 9.85936L7.25891 6.0939C7.24261 5.63713 7.60849 5.25797 8.06555 5.25797C8.5232 5.25797 8.8893 5.63808 8.87213 6.09541L8.73076 9.86062ZM7.21926 11.7574C7.26412 11.6666 7.32814 11.5851 7.41131 11.513C7.46859 11.4633 7.52999 11.4215 7.59553 11.3876C7.73661 11.3146 7.89683 11.278 8.07618 11.278C8.26676 11.278 8.43415 11.3187 8.57836 11.3999C8.6354 11.4321 8.68881 11.4706 8.73859 11.5154C8.81988 11.5886 8.88236 11.6708 8.92603 11.7618C8.97673 11.8675 9.00208 11.9853 9.00208 12.115C9.00208 12.2317 8.981 12.3396 8.93885 12.4386C8.89617 12.5388 8.83188 12.63 8.74598 12.7121C8.66424 12.7902 8.5746 12.8496 8.47706 12.8903C8.37086 12.9346 8.25529 12.9568 8.13036 12.9568C7.86357 12.9568 7.64481 12.8932 7.47407 12.7659C7.45149 12.7491 7.42975 12.7311 7.40885 12.7121C7.31197 12.6238 7.24131 12.5249 7.19688 12.4155C7.15926 12.3229 7.14044 12.2227 7.14044 12.115C7.14044 11.983 7.16671 11.8638 7.21926 11.7574Z" fill="rgb(255,255,255)" fill-rule="evenodd" />
</svg>
<template>
<div class="warnning-pane-wrapper" :style="{ width: width ? width : '1600px', height: height ? height : '116px' }"
:class="{
level1: warnningLevel === '特别重大风险',
level2: warnningLevel === '重大风险',
level3: warnningLevel === '较大风险',
level4: warnningLevel === '一般风险',
}" @click="handleClickPane">
<div class="warnning-pane-header">
<div class="header-left" :class="{
'header-left-level1': warnningLevel === '特别重大风险',
'header-left-level2': warnningLevel === '重大风险',
'header-left-level3': warnningLevel === '较大风险',
'header-left-level4': warnningLevel === '一般风险',
}">
<div class="warnning-icon">
<img src="./icons/warnning-icon.svg" alt="">
</div>
<div class="warnning-text text-title-3-bold">{{ warnningLevel }}</div>
</div>
<div class="header-right">
<img v-if="warnningLevel === '特别重大风险'" src="./icons/level1.svg" alt="">
<img v-else-if="warnningLevel === '重大风险'" src="./icons/level2.svg" alt="">
<img v-else-if="warnningLevel === '较大风险'" src="./icons/level3.svg" alt="">
<img v-else-if="warnningLevel === '一般风险'" src="./icons/level4.svg" alt="">
<img v-else src="./icons/level5.svg" alt="">
</div>
</div>
<div class="warnning-pane-content text-regular">
{{ warnningContent }}
</div>
</div>
</template>
<script setup>
import { ElMessage } from 'element-plus'
const props = defineProps(
{
warnningContent: {
type: String,
default: ''
},
warnningLevel: {
type: String,
default: '低风险'
}
}
)
const emit = defineEmits(['clickPane'])
const handleClickPane = () => {
ElMessage.success('点击了预警面板')
emit('clickPane')
}
</script>
<style lang="scss" scoped>
.warnning-pane-wrapper {
border-radius: 10px;
border: 1px solid var(--color-primary-100);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
cursor: pointer;
}
.level1 {
border: 1px solid var(--color-red-100) !important;
}
.level2 {
border: 1px solid var(--color-orange-100) !important;
}
.level3 {
border: 1px solid var(--color-yellow-100) !important;
}
.level4 {
border: 1px solid var(--color-green-100) !important;
}
.warnning-pane-header {
height: 44px;
display: flex;
justify-content: space-between;
}
.header-left {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
width: 164px;
height: 32px;
background: var(--color-main-active);
border-radius: 10px 0 10px 0;
}
.header-left-level1 {
background: var(--color-red-100) !important;
}
.header-left-level2 {
background: var(--color-orange-100) !important;
}
.header-left-level3 {
background: var(--color-yellow-100) !important;
}
.header-left-level4 {
background: var(--color-green-100) !important;
}
.warnning-icon {
width: 16px;
height: 16px;
img {
width: 100%;
height: 100%;
}
}
.warnning-text {
color: #fff;
}
.header-right {
width: 16px;
height: 16px;
margin-top: 17px;
margin-right: 16px;
img {
width: 100%;
height: 100%;
}
}
.warnning-pane-content{
width: calc(100% - 40px);
margin: 0 auto;
height: 60px;
display: -webkit-box;
/* 2. 设置内部布局方向为垂直 */
-webkit-box-orient: vertical;
/* 3. 限制显示的行数为 2 行 */
-webkit-line-clamp: 2;
/* 4. 隐藏超出部分 */
overflow: hidden;
/* 5. 设置文本溢出显示省略号 */
text-overflow: ellipsis;
}
</style>
\ No newline at end of file
...@@ -47,21 +47,38 @@ const props = defineProps({ ...@@ -47,21 +47,38 @@ const props = defineProps({
showAllBtn: { showAllBtn: {
type: Boolean, type: Boolean,
default: true default: true
},
// 当业务功能尚未实现时,点击右上角图标仅弹出统一提示
devTip: {
type: Boolean,
default: false
} }
}) })
const handleSave = () => { const handleSave = () => {
if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!')
return
}
ElMessage.success('保存当前内容') ElMessage.success('保存当前内容')
// emit('save') // emit('save')
} }
const handleDownload = () => { const handleDownload = () => {
if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!')
return
}
ElMessage.success('下载当前内容') ElMessage.success('下载当前内容')
// emit('download') // emit('download')
} }
const handleCollect = () => { const handleCollect = () => {
if (props.devTip) {
ElMessage.warning('当前功能正在开发中,敬请期待!')
return
}
ElMessage.success('收藏当前内容') ElMessage.success('收藏当前内容')
// emit('collect') // emit('collect')
...@@ -122,7 +139,7 @@ const emit = defineEmits(['save', 'download', 'collect']) ...@@ -122,7 +139,7 @@ const emit = defineEmits(['save', 'download', 'collect'])
.header-btn1 { .header-btn1 {
position: absolute; position: absolute;
top: 14px; top: 14px;
right: 104px; right: 116px;
} }
.header-right { .header-right {
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { size } from 'lodash';
import { ref, computed, watch, onMounted } from 'vue'; import { ref, computed, watch, onMounted } from 'vue';
// 组件属性 // 组件属性
...@@ -21,9 +20,10 @@ const props = defineProps({ ...@@ -21,9 +20,10 @@ const props = defineProps({
// SVG颜色 // SVG颜色
color: { color: {
type: String, type: String,
default: '#000' default: null
} }
, size: { ,
size: {
type: Number, type: Number,
default: null default: null
} }
...@@ -43,42 +43,46 @@ const processedSvgContent = computed(() => { ...@@ -43,42 +43,46 @@ const processedSvgContent = computed(() => {
// 替换SVG中的颜色 // 替换SVG中的颜色
let processed = svgContent.value; let processed = svgContent.value;
if (props.color) {
// 替换fill属性
processed = processed.replace(/fill="([^"]*)"/g, (match, p1) => {
// 保留透明和none值
if (p1 === 'none' || p1 === 'transparent') {
return match;
}
return `fill="${props.color}"`;
});
// 替换stroke属性
processed = processed.replace(/stroke="([^"]*)"/g, (match, p1) => {
// 保留透明和none值
if (p1 === 'none' || p1 === 'transparent') {
return match;
}
return `stroke="${props.color}"`;
});
}
// 替换fill属性 if (props.size) {
processed = processed.replace(/fill="([^"]*)"/g, (match, p1) => { // 替换width属性
// 保留透明和none值 processed = processed.replace(/width="([^"]*)"/g, (match, p1) => {
if (p1 === 'none' || p1 === 'transparent') { if (props.size !== null) {
return `width="${props.size}"`;
}
return match; return match;
} });
return `fill="${props.color}"`;
});
// 替换stroke属性 // 替换height属性
processed = processed.replace(/stroke="([^"]*)"/g, (match, p1) => { processed = processed.replace(/height="([^"]*)"/g, (match, p1) => {
// 保留透明和none值 if (props.size !== null) {
if (p1 === 'none' || p1 === 'transparent') { return `height="${props.size}"`;
}
return match; return match;
} });
return `stroke="${props.color}"`; }
});
// 替换width属性
processed = processed.replace(/width="([^"]*)"/g, (match, p1) => {
if (props.size !== null) {
return `width="${props.size}"`;
}
return match;
});
// 替换height属性
processed = processed.replace(/height="([^"]*)"/g, (match, p1) => {
if (props.size !== null) {
return `height="${props.size}"`;
}
return match;
});
console.log(processed) // console.log(processed)
return processed; return processed;
}); });
...@@ -131,10 +135,14 @@ onMounted(() => { ...@@ -131,10 +135,14 @@ onMounted(() => {
<style scoped> <style scoped>
.color-svg { .color-svg {
display: inline-block; display: inline-block;
/* svg垂直居中 */
vertical-align: middle;
} }
.svg-container { .svg-container {
display: inline-block; display: flex;
align-items: center;
justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
......
...@@ -10,11 +10,10 @@ ...@@ -10,11 +10,10 @@
</div> </div>
<!-- <div class="more" @click="handleToMoreNews">{{ "更多 +" }}</div> --> <!-- <div class="more" @click="handleToMoreNews">{{ "更多 +" }}</div> -->
</div> </div>
<div class="msg-bubble-main"> <div class="msg-bubble-main" ref="scrollContainer">
<div class="message-bubble" v-for="(item, index) in messageList" :key="index" @click="handleClickPerson(item)"> <div class="message-bubble" v-for="(item, index) in displayList" :key="index" @click="handleClickPerson(item)">
<div class="avatar-container"> <div class="avatar-container">
<img :src="item[props.imageUrl] || avatarUser" :alt="item[props.name]" class="avatar" /> <img :src="item[props.imageUrl] || avatarUser" :alt="item[props.name]" class="avatar" />
<div class="avatar-containerOne" v-if="isRepublicanParty"> <div class="avatar-containerOne" v-if="isRepublicanParty">
<img src="./image2.png" alt="" class="avatar-imageOne" /> <img src="./image2.png" alt="" class="avatar-imageOne" />
</div> </div>
...@@ -35,27 +34,12 @@ ...@@ -35,27 +34,12 @@
</div> </div>
</div> </div>
</div> </div>
<!-- <MessageBubble v-for="(item, index) in messageList" @click="handleClickPsserson(item)"
@info-click="handleMediaClick(item)" :key="index" :avatar="item.img ? item.img : DefaultIcon1" :name="item.name"
:time="item.time" :source="item.source" :content="item.content" /> -->
<!-- <div class="msg-bubble-main-item" v-for="(item, index) in messageList" :key="index">
<div class="left" @click="handleClickPerson(item)">
<img :src="item.img ? item.img : DefaultIcon1" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="name">{{ item.name }}</div>
<div class="time">{{ item.time }}</div>
</div>
<div class="content">{{ item.content }}</div>
</div>
</div> -->
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed } from "vue"; import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import avatarUser from "@/assets/images/avatar_user.png"; import avatarUser from "@/assets/images/avatar_user.png";
const emit = defineEmits(["click", "info-click"]); const emit = defineEmits(["click", "info-click"]);
...@@ -118,6 +102,66 @@ const handleInfoClick = item => { ...@@ -118,6 +102,66 @@ const handleInfoClick = item => {
const handleToMoreNews = item => { const handleToMoreNews = item => {
emit("more-click", item); emit("more-click", item);
}; };
const scrollContainer = ref(null)
let timer = null
const currentIndex = ref(0)
// const itemHeight = ref(80) // 每条消息的高度,需要根据实际调整
// 计算当前显示的消息列表(只显示固定数量的消息)
const displayList = computed(() => {
if(props.messageList.length < 4) {
return props.messageList
}
// 确保 messageList 存在且有数据
if (!props.messageList || !Array.isArray(props.messageList) || props.messageList.length === 0) {
return []
}
const list = []
const totalLength = props.messageList.length
for (let i = 0; i < 3; i++) {
// 计算当前索引,确保不会超出数组长度
const index = (currentIndex.value + i) % totalLength
const item = props.messageList[index]
// 确保 item 存在再添加到列表
if (item) {
list.push(item)
}
}
return list
})
// 开始滚动
const startScroll = () => {
if (timer) clearInterval(timer)
timer = setInterval(() => {
currentIndex.value = (currentIndex.value + 1) % props.messageList.length
}, 2000) // 每秒滚动一条
}
// 停止滚动
const stopScroll = () => {
if (timer) {
clearInterval(timer)
timer = null
}
}
onMounted(() => {
if (props.messageList.length > 3) {
startScroll()
}
})
onBeforeUnmount(() => {
stopScroll()
})
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
...@@ -180,7 +224,7 @@ const handleToMoreNews = item => { ...@@ -180,7 +224,7 @@ const handleToMoreNews = item => {
.msg-bubble-main { .msg-bubble-main {
height: 402px; height: 402px;
overflow-y: auto; overflow: hidden;
box-sizing: border-box; box-sizing: border-box;
padding-bottom: 8px; padding-bottom: 8px;
padding-left: 21px; padding-left: 21px;
...@@ -190,6 +234,8 @@ const handleToMoreNews = item => { ...@@ -190,6 +234,8 @@ const handleToMoreNews = item => {
display: flex; display: flex;
max-width: 740px; max-width: 740px;
margin-bottom: 15px; margin-bottom: 15px;
transition: transform 2s ease;
/* 可选:添加平滑动画 */
.avatar-container { .avatar-container {
flex-shrink: 0; flex-shrink: 0;
...@@ -317,6 +363,18 @@ const handleToMoreNews = item => { ...@@ -317,6 +363,18 @@ const handleToMoreNews = item => {
} }
} }
// .msg-bubble-main {
// height: 400px; /* 设置固定高度 */
// overflow: hidden;
// position: relative;
// }
// .message-bubble {
// transition: transform 0.3s ease; /* 可选:添加平滑动画 */
// height: 80px; /* 固定每条消息高度 */
// margin-bottom: 10px; /* 消息之间的间距 */
// }
/* 响应式设计 */ /* 响应式设计 */
@media (max-width: 768px) { @media (max-width: 768px) {
.message-bubble { .message-bubble {
......
...@@ -9,14 +9,9 @@ ...@@ -9,14 +9,9 @@
<SearchBar v-show="isShowSearchBar" /> <SearchBar v-show="isShowSearchBar" />
<div class="title-box" v-show="!isShowSearchBar"> <div class="title-box" v-show="!isShowSearchBar">
<!-- <div class="title-box" v-if="false"> --> <!-- <div class="title-box" v-if="false"> -->
<div <div class="title" v-for="(item, index) in homeTitleList" :key="index"
class="title" @mouseenter="handleShowMenu(index, true)" @mouseleave="handleShowMenu(index, false)"
v-for="(item, index) in homeTitleList" @click="handleClickTitle(item)">
:key="index"
@mouseenter="handleShowMenu(index, true)"
@mouseleave="handleShowMenu(index, false)"
@click="handleClickTitle(item)"
>
<div class="text" :class="{ textActive: homeActiveTitleIndex === index }"> <div class="text" :class="{ textActive: homeActiveTitleIndex === index }">
{{ item.name }} {{ item.name }}
</div> </div>
...@@ -36,7 +31,8 @@ ...@@ -36,7 +31,8 @@
<div class="name">{{ "管理员" }}</div> <div class="name">{{ "管理员" }}</div>
</div> </div>
</div> </div>
<div class="menu-box" v-show="isShowMenu" @mouseenter="handleHoverMenu(true)" @mouseleave="handleHoverMenu(false)"> <div class="menu-box" v-show="isShowMenu" @mouseenter="handleHoverMenu(true)"
@mouseleave="handleHoverMenu(false)">
<div class="menu-content"> <div class="menu-content">
<div class="menu-item" v-for="(item, index) in menuList" :key="index" @click="handleToModule(item)"> <div class="menu-item" v-for="(item, index) in menuList" :key="index" @click="handleToModule(item)">
<div class="icon"> <div class="icon">
...@@ -90,7 +86,7 @@ const handleGetPersonType = async () => { ...@@ -90,7 +86,7 @@ const handleGetPersonType = async () => {
personTypeList.value = []; personTypeList.value = [];
} }
window.sessionStorage.setItem("personTypeList", JSON.stringify(personTypeList.value)); window.sessionStorage.setItem("personTypeList", JSON.stringify(personTypeList.value));
} catch (error) {} } catch (error) { }
}; };
// 概览页标题列表 // 概览页标题列表
...@@ -230,6 +226,7 @@ onMounted(() => { ...@@ -230,6 +226,7 @@ onMounted(() => {
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: linear-gradient(180deg, rgba(246, 250, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%); background: linear-gradient(180deg, rgba(246, 250, 255, 0.8) 0%, rgba(255, 255, 255, 0.8) 100%);
padding: 12px 0; padding: 12px 0;
.nav-content { .nav-content {
width: 1600px; width: 1600px;
margin: 0 auto; margin: 0 auto;
...@@ -237,11 +234,14 @@ onMounted(() => { ...@@ -237,11 +234,14 @@ onMounted(() => {
justify-content: space-between; justify-content: space-between;
position: relative; position: relative;
align-items: flex-start; align-items: flex-start;
.nav-left { .nav-left {
display: flex; display: flex;
align-items: center; align-items: center;
&.flex-start { &.flex-start {
align-items: flex-start; align-items: flex-start;
.icon { .icon {
flex-shrink: 0; flex-shrink: 0;
width: 48px; width: 48px;
...@@ -251,16 +251,19 @@ onMounted(() => { ...@@ -251,16 +251,19 @@ onMounted(() => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
img { img {
width: 29px; width: 29px;
height: 30px; height: 30px;
} }
} }
} }
.icon { .icon {
width: 29px; width: 29px;
height: 30px; height: 30px;
margin-right: 17px; margin-right: 17px;
img { img {
width: 100%; width: 100%;
height: 100%; height: 100%;
...@@ -274,6 +277,7 @@ onMounted(() => { ...@@ -274,6 +277,7 @@ onMounted(() => {
.title { .title {
cursor: pointer; cursor: pointer;
position: relative; position: relative;
&:hover { &:hover {
.text { .text {
color: var(--color-main-active); color: var(--color-main-active);
...@@ -299,6 +303,7 @@ onMounted(() => { ...@@ -299,6 +303,7 @@ onMounted(() => {
width: 90%; width: 90%;
height: 20px; height: 20px;
margin-top: 9px; margin-top: 9px;
&::after { &::after {
display: block; display: block;
content: ""; content: "";
...@@ -365,23 +370,35 @@ onMounted(() => { ...@@ -365,23 +370,35 @@ onMounted(() => {
} }
.menu-box { .menu-box {
// position: absolute;
// z-index: 999999999;
// width: 713px;
// height: 413px;
// top: 52px;
// left: 0;
// box-sizing: border-box;
// border-radius: 10px;
// backdrop-filter: blur(10px);
// -webkit-backdrop-filter: blur(10px);
// box-shadow: 0px 8px 32px 0px rgba(31, 38, 135, 0.15);
// background: rgba(255, 255, 255, 0.25);
// backdrop-filter: blur(10px);
// -webkit-backdrop-filter: blur(10px);
// border: 1px solid rgba(255, 255, 255, 0.3);
// background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
// box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
position: absolute; position: absolute;
z-index: 999999999; z-index: 999999;
width: 713px; width: 713px;
height: 413px; height: 413px;
top: 52px; top: 52px;
left: 0; left: -2px;
box-sizing: border-box; box-sizing: border-box;
border: 1px solid rgba(255, 255, 255, 1);
border-radius: 10px; border-radius: 10px;
backdrop-filter: blur(10px); backdrop-filter: blur(30px);
-webkit-backdrop-filter: blur(10px); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
box-shadow: 0px 8px 32px 0px rgba(31, 38, 135, 0.15); background: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.3);
background: linear-gradient(135deg, rgba(255, 255, 255, 0.1) 0%, rgba(255, 255, 255, 0.2) 100%);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.2);
.menu-content { .menu-content {
width: 562px; width: 562px;
......
<template> <template>
<div class="box3-item" @click="handleToNewsAnalysis(news)"> <el-space alignment="flex-start" :size="10">
<div class="left"> <img :width="64" :height="52" :src="news ?? DefaultIconNews" alt="" />
<img :src="news[props.img] ? news[props.img] : DefaultIconNews" alt="" /> <el-space direction="vertical" alignment="flex-start" :size="0">
</div> <common-text :line-limit="1" class="text-regular text-hover" color="var(--text-primary-80-color)">
<div class="right"> {{ title }}
<div class="right-top"> </common-text>
<div class="title"><span class="text-inner">{{ news[props.title] }}</span></div> <common-text :line-limit="1" class="text-tip-1" color="var(--text-primary-65-color)">
<div class="time">{{ news[props.from] }}</div> {{ from }}
</div> </common-text>
<div class="right-footer">{{ news[props.content] }}</div> </el-space>
</div> </el-space>
</div>
</template> </template>
<script setup> <script setup>
import DefaultIconNews from "@/assets/icons/default-icon-news.png"; import DefaultIconNews from "@/assets/icons/default-icon-news.png";
const props = defineProps({ import { ElSpace } from "element-plus";
import CommonText from "../texts/CommonText.vue";
// 新闻列表数据
news: {
type: Object,
default: () => { }
},
const props = defineProps({
img: { img: {
type: String, type: String,
default: 'img' default: ''
}, },
title: { title: {
type: String, type: String,
default: "title" default: ""
}, },
from: { from: {
type: String, type: String,
default: "from" default: ""
}, },
content: { content: {
type: String, type: String,
default: "content" default: ""
}, },
}); });
...@@ -49,72 +44,4 @@ const handleToNewsAnalysis = (item, index) => { ...@@ -49,72 +44,4 @@ const handleToNewsAnalysis = (item, index) => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@use '@/styles/common.scss'; @use '@/styles/common.scss';
.box3-item {
display: flex;
align-items: center;
height: 78px;
margin: 0px 21px;
cursor: pointer;
&:hover {
.right-top .title {
color: rgb(5, 95, 194) !important;
font-weight: 700;
}
.right-top .text-inner {
border-bottom-color: rgb(5, 95, 194) !important;
}
}
}
.left {
width: 97px;
// flex-shrink: 0;
height: 72px;
img {
width: 100%;
height: 100%;
border-radius: 4px;
}
}
.right {
flex: 1;
min-width: 0;
margin-left: 20px;
.right-top {
display: flex;
justify-content: space-between;
.title {
// width: 500px;
@extend .text-title-3-bold;
color: var(--text-primary-80-color);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.text-inner {
border-bottom: 1px solid transparent;
}
}
.time {
text-align: right;
@extend .text-tip-2;
color: var(--text-primary-65-color);
}
}
.right-footer {
@extend .text-compact;
color: var(--text-primary-65-color);
@include common.text-ellipsis(2);
}
}
</style> </style>
\ No newline at end of file
<template>
<div class="news-item">
<el-space direction="vertical" class="flex-fill" alignment='flex-start'>
<common-text :lineLimit="1" class="text-bold" color="var(--text-primary-80-color)">{{
props.title
}}</common-text>
<common-text class="text-tip-2" color="var(--text-primary-65-color)">
{{ props.from }}
</common-text>
<el-space v-if="props.aeraTags">
<area-tag v-for="(tag, index) in props.aeraTags" :key="index" :tagName="tag" />
</el-space>
</el-space>
<img style="width: 122px; height: 82px" :src="props.img">
<!-- <img v-else style="width: 122px; height: 82px" :src="DefaultIconNews"> -->
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElSpace } from 'element-plus'
import AreaTag from '@/components/base/AreaTag/index.vue'
import DefaultIconNews from "@/assets/icons/default-icon-news.png";
import CommonText from '../texts/CommonText.vue';
const props = defineProps({
img: {
type: String,
default: 'img'
},
title: {
type: String,
default: "title"
},
from: {
type: String,
default: "from"
},
aeraTags: {
type: Array,
default: []
},
content: {
type: String,
default: "content"
},
});
const emit = defineEmits(['item-click', 'more-click']);
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
.news-item {
display: flex;
margin-top: 10px;
}
</style>
\ No newline at end of file
<script setup>
</script>
<template> <template>
<div class="flex-display box"> <div class="flex-display box">
<div class="img"></div> <div class="img">
<img src="@/assets/icons/box-footer-left-icon.png" alt="">
</div>
<div class="flex-fill txt text-tip-1"> <div class="flex-fill txt text-tip-1">
<slot></slot> <slot></slot>
</div> </div>
<div class="arrow"><span></span></div> <div class="arrow">
<img src="@/assets/icons/box-footer-right-icon.png" alt="">
</div>
</div> </div>
</template> </template>
<script setup>
</script>
<style scoped lang="scss"> <style scoped lang="scss">
@use '@/styles/common.scss'; @use '@/styles/common.scss';
...@@ -29,7 +33,11 @@ ...@@ -29,7 +33,11 @@
.img { .img {
width: 19px; width: 19px;
height: 20px; height: 20px;
background-image: url("@/assets/icons/model.png");
img {
width: 100%;
height: 100%;
}
} }
.txt { .txt {
...@@ -42,21 +50,12 @@ ...@@ -42,21 +50,12 @@
} }
.arrow { .arrow {
border-radius: 50%;
min-width: 24px;
width: 24px; width: 24px;
height: 24px; height: 24px;
background: var(--color-primary-10);
display: flex;
align-items: center;
justify-content: center;
span { img {
font-size: 22px; width: 100%;
font-weight: bold; height: 100%;
position: relative;
top: -3px;
/* 向上偏移2px */
} }
} }
</style> </style>
\ No newline at end of file
<template> <template>
<el-space :size="16" class="text-tip-1-bold box"> <el-space :size="16" class="text-tip-1-bold box-color-prefix">
<div class="color-prefix"></div> <div class="color-prefix"></div>
<slot></slot> <slot></slot>
</el-space> </el-space>
...@@ -11,6 +11,10 @@ const props = defineProps({ ...@@ -11,6 +11,10 @@ const props = defineProps({
color: { color: {
type: String, type: String,
default: 'var(--color-primary-100)' default: 'var(--color-primary-100)'
},
height: {
type: String,
default: '16px'
} }
}) })
</script> </script>
...@@ -18,11 +22,11 @@ const props = defineProps({ ...@@ -18,11 +22,11 @@ const props = defineProps({
<style lang="scss" scoped> <style lang="scss" scoped>
.color-prefix { .color-prefix {
width: 8px; width: 8px;
height: 16px; height: v-bind(height);
background-color: v-bind(color); background-color: v-bind(color);
} }
.box { .box-color-prefix {
color: v-bind(color); color: v-bind(color);
} }
</style> </style>
\ No newline at end of file
<template>
<div class="common-text-box-hudf">
<slot></slot>
</div>
</template>
<script setup lang="js">
const props = defineProps({
color: {
type: String,
default: "#000"
},
lineLimit: {
type: Number,
default: null
}
});
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
.common-text-box-hudf {
color: v-bind(color) !important;
@if('v-bind(lineLimit) !==null') {
@include common.text-ellipsis(v-bind(lineLimit));
}
}
</style>
<template>
<p class="p-regular-rereg">
<span class="text-regular" v-for="(segment, index) in processedText" :key="index">
<a v-if="segment.isEntity" :href="`https://cn.bing.com/search?q=${segment.entity?.text_span}`"
class="entity-link" target="_blank" rel="noopener noreferrer">
{{ segment.entity?.text_span }}
<img :src="SearchIcon" :width="10" :height="10" alt="search" />
</a>
<span v-else>
{{ segment.text }}
</span>
</span>
</p>
</template>
<script lang="ts" setup>
import { TextEntity } from '@/api/intelligent';
import { ref, watch, onMounted } from 'vue';
import SearchIcon from './images/search.png'
export interface ProcessedTextSegment {
text: string
isEntity: boolean
entity?: TextEntity
}
const props = defineProps({
text: {
type: String,
default: ''
}
, entities: {
type: Array<TextEntity>,
default: () => []
}
})
// 处理后的文本段
const processedText = ref<ProcessedTextSegment[]>([])
// 处理文本,识别并替换实体
const processText = () => {
if (!props.text || !props.entities) {
console.log('props.text', props.entities.length)
processedText.value = [{ text: '', isEntity: false }]
return
}
const result = []
let currentPosition = 0
// 按实体文本长度排序,优先匹配长文本
const sortedEntities = [...props.entities].sort((a, b) =>
b.text_span.length - a.text_span.length
)
while (currentPosition < props.text.length) {
let matched = false
for (const entity of sortedEntities) {
const entityText = entity.text_span
const endPosition = currentPosition + entityText.length
if (props.text.substring(currentPosition, endPosition) === entityText) {
// 如果当前位置是实体,添加到结果
result.push({
isEntity: true,
entity: { ...entity }
})
currentPosition = endPosition
matched = true
break
}
}
if (!matched) {
// 如果不是实体,收集普通文本
let nextEntityStart = props.text.length
for (const entity of sortedEntities) {
const pos = props.text.indexOf(entity.text_span, currentPosition)
if (pos !== -1 && pos < nextEntityStart) {
nextEntityStart = pos
}
}
if (nextEntityStart > currentPosition) {
const plainText = props.text.substring(currentPosition, nextEntityStart)
result.push({
text: plainText,
isEntity: false
})
currentPosition = nextEntityStart
} else {
// 没有更多实体,添加剩余文本
const remainingText = props.text.substring(currentPosition)
if (remainingText) {
result.push({
text: remainingText,
isEntity: false
})
}
currentPosition = props.text.length
}
}
}
processedText.value = result
}
// 监听文本和实体变化
watch(() => props.text, processText)
watch(() => props.entities, processText, { deep: true })
// 初始化处理
onMounted(processText)
</script>
<style lang="scss" scoped>
@use '@/styles/common.scss';
.entity-link {
color: var(--color-primary-100);
}
.p-regular-rereg {
text-indent: 2em;
}
</style>
\ No newline at end of file
import { useRouter } from "vue-router";
export function useGotoPage() {
const router = useRouter();
return (path, data, isNewTabs = true) => {
console.log('path', path);
if (isNewTabs) {
// 打开新页面
const url = new URL(window.location.origin + path);
if (data) {
Object.entries(data).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
}
window.open(url.toString(), '_blank');
} else {
// 当前页面打开
router.push({ path, query: data });
}
}
}
\ No newline at end of file
...@@ -13,6 +13,7 @@ const BillInfluenceLayout = () => import('@/views/bill/influence/index.vue') ...@@ -13,6 +13,7 @@ const BillInfluenceLayout = () => import('@/views/bill/influence/index.vue')
const BillInfluenceIndustry = () => import('@/views/bill/influence/industry/index.vue') const BillInfluenceIndustry = () => import('@/views/bill/influence/industry/index.vue')
const BillInfluenceScientificResearch = () => import('@/views/bill/influence/scientificResearch/index.vue') const BillInfluenceScientificResearch = () => import('@/views/bill/influence/scientificResearch/index.vue')
const BillRelevantCircumstance = () => import('@/views/bill/relevantCircumstance/index.vue') const BillRelevantCircumstance = () => import('@/views/bill/relevantCircumstance/index.vue')
const BillOriginalText = () => import('@/views/bill/billOriginalText/index.vue')
const billRoutes = [ const billRoutes = [
...@@ -35,6 +36,14 @@ const billRoutes = [ ...@@ -35,6 +36,14 @@ const billRoutes = [
dynamicTitle: true // 标记需要动态设置标题 dynamicTitle: true // 标记需要动态设置标题
}, },
children: [ children: [
{
path: "originalText",
name: "BillOriginalText",
component: BillOriginalText,
meta: {
title: "法案原文"
}
},
// 法案分析路由 // 法案分析路由
{ {
path: "bill", path: "bill",
......
const newsBrief = () => import('@/views/newsBrief/index.vue') const newsBrief = () => import('@/views/newsBrief/index.vue')
const ModeuleNews = () => import('@/views/newsBrief/ModeuleNews.vue')
const NewsDetial = () => import('@/views/newsBrief/NewsDetial.vue')
const NewsAnalysis = () => import('@/views/newsAnalysis/index.vue') const NewsAnalysis = () => import('@/views/newsAnalysis/index.vue')
const newsRoutes = [ const newsRoutes = [
...@@ -11,6 +13,25 @@ const newsRoutes = [ ...@@ -11,6 +13,25 @@ const newsRoutes = [
title: "新闻速览" title: "新闻速览"
} }
}, },
//新闻模块页面路由
{
path: "/newsModeule/:id",
name: "newsModeule",
component: ModeuleNews,
meta: {
title: "新闻模块"
}
},
//新闻详情页面路由
{
path: "/newsDetail/:id",
name: "newsDetail",
component: NewsDetial,
meta: {
title: "新闻详情"
}
},
// 新闻事件分析 // 新闻事件分析
{ {
...@@ -22,5 +43,21 @@ const newsRoutes = [ ...@@ -22,5 +43,21 @@ const newsRoutes = [
} }
} }
]; ];
import { useGotoPage } from "../common.js";
export function useGotoNewsBrief() {
const gotoPage = useGotoPage();
return (isNewTabs = true) => gotoPage("/newsBrief/", {}, isNewTabs)
}
export function useGotoNewsDetail() {
const gotoPage = useGotoPage();
return (id, isNewTabs = true) => gotoPage("/newsDetail/" + id, {}, isNewTabs)
}
export function useGotoNewsModule() {
const gotoPage = useGotoPage();
return (id, name, isNewTabs = true) =>
gotoPage("/newsModeule/" + id, { name }, isNewTabs)
}
export default newsRoutes; export default newsRoutes;
...@@ -11,19 +11,19 @@ ...@@ -11,19 +11,19 @@
display: flex; display: flex;
} }
.flex-display-center{ .flex-display-center {
@extend .flex-display; @extend .flex-display;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
.flex-display-end{ .flex-display-end {
@extend .flex-display; @extend .flex-display;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
} }
.flex-display-start{ .flex-display-start {
@extend .flex-display; @extend .flex-display;
justify-content: flex-start; justify-content: flex-start;
align-items: center; align-items: center;
...@@ -37,6 +37,13 @@ ...@@ -37,6 +37,13 @@
width: 100%; width: 100%;
} }
.mouse-hover:hover {
cursor: pointer;
box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.082);
// margin-top: 1px;
// margin-left: 1px;
}
// 文本超出指定行数省略号显示 // 文本超出指定行数省略号显示
@mixin text-ellipsis($line-clamp) { @mixin text-ellipsis($line-clamp) {
overflow: hidden; overflow: hidden;
...@@ -64,6 +71,13 @@ ...@@ -64,6 +71,13 @@
color: var(--text-primary-80); color: var(--text-primary-80);
} }
.text-hover {
&:hover {
color: rgb(5, 95, 194) !important;
font-weight: 700;
}
}
//0级标题 //0级标题
.text-title-0 { .text-title-0 {
@extend .text-base; @extend .text-base;
......
...@@ -14,12 +14,30 @@ const span = 12 ...@@ -14,12 +14,30 @@ const span = 12
</template> </template>
`}} `}}
</pre> </pre>
<div class="background-as-card"> <div class="common-padding">
<div v-for="item in [1, 2, 3, 4]" :key="item"> <div class="background-as-card">
{{ item }} <div v-for="item in [1, 2, 3, 4]" :key="item">
{{ item }}
</div>
</div> </div>
</div> </div>
</el-col> </el-col>
<el-col :span="span">
<pre>{{ `import '@/styles/container.scss';\n<template>
<div class="common-page"></div>
</template>
`}}
</pre>
通用的页面容器,限定了页面宽度为1600px,居中显示
</el-col>
<el-col :span="span">
<pre>{{ `import '@/styles/common.scss';\n<template>
<div class="mouse-hover"></div>
</template>
`}}
</pre>
<div class="mouse-hover">鼠标悬停</div>
</el-col>
</el-row> </el-row>
</template> </template>
......
<template>
<el-row class="wrapper layout-grid-line">
<el-col :span="span">
<pre>
{{
`import GraphChart from '@/components/base/GraphChart/index.vue';
<template>
<GraphChart :nodes="nodes" :links="links" layoutType="none">
</GraphChart>
</template>
`}}
</pre>
<div class="chart-box">
<GraphChart :nodes="nodes" :links="links" layoutType="none">
</GraphChart>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { ref } from 'vue'
import GraphChart from '@/components/base/GraphChart/index.vue'
import CompanyImg from "./symbol.png"
const span = 24
const nodes = ref([
{
id: 0,
name: "泰丰先行",
// category: 0,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
x: 50,
y: 10
},
{
id: 1,
name: "国轩高科",
// category: 0,
symbolSize: 30,
value: 9,
symbol: `image://${CompanyImg}`,
x: 150,
y: 10
},
{
id: 2,
name: "智方纳米",
// category: 2,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
x: 250,
y: 10
},
{
id: 3,
name: "香百科技",
// category: 1,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
x: 350,
y: 10
},
{
id: 4,
name: "格林滨",
// category: 2,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
x: 450,
y: 10
},
{
id: 5,
name: "江西紫宸",
// category: 2,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
x: 550,
y: 10
},
{
id: 6,
name: "紫江企业",
// category: 4,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
x: 650,
y: 10
},
{
id: 7,
name: "大而美法案",
// category: 4,
symbolSize: 50,
value: 5,
symbol: `image://${CompanyImg}`,
x: 300,
y: 200
},
{
id: 8,
name: "比亚迪",
// category: 0,
symbolSize: 30,
value: 10,
symbol: `image://${CompanyImg}`,
x: 50,
y: 400
},
{
id: 9,
name: "铜陵有色",
// category: 3,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
x: 150,
y: 400
},
{
id: 10,
name: "长盛精密",
// category: 1,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
x: 250,
y: 400
},
{
id: 11,
name: "天合光能",
// category: 0,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
x: 350,
y: 400
},
{
id: 12,
name: "昆仑化学",
// category: 2,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
x: 250,
y: 400
},
{
id: 13,
name: "嘉源科技",
// category: 1,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
x: 450,
y: 400
},
{
id: 14,
name: "华阳集团",
// category: 4,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
x: 550,
y: 400
},
{
id: 15,
name: "海辰智能",
// category: 1,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
x: 650,
y: 400
},
]);
const links = ref([
{ source: 1, target: 7, label: { show: true, formatter: '合作' } },
{ source: 2, target: 7, label: { show: true, formatter: '持股' } },
{ source: 3, target: 7, label: { show: true, formatter: '合作' } },
{ source: 4, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '从属' } },
{ source: 5, target: 7, label: { show: true, formatter: '合作' } },
{ source: 6, target: 7, label: { show: true, formatter: '持股' } },
{ source: 0, target: 7, label: { show: true, formatter: '持股' } },
{ source: 8, target: 7, label: { show: true, formatter: '合作' } },
{ source: 9, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '从属' } },
{ source: 10, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '合作' } },
{ source: 11, target: 7, label: { show: true, formatter: '合作' } },
{ source: 12, target: 7, label: { show: true, formatter: '合作' } },
{ source: 13, target: 7, label: { show: true, formatter: '合作' } },
{ source: 14, target: 7, label: { show: true, formatter: '合作' } },
{ source: 15, target: 7, label: { show: true, formatter: '合作', color: 'red', borderColor: 'red' } },
]);
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
}
.chart-box {
width: 800px;
height: 500px;
}
</style>
\ No newline at end of file
<template>
<el-row class="wrapper layout-grid-line">
<el-col :span="span">
<pre>
{{
`import GraphChart from '@/components/base/GraphChart/index.vue';
<template>
<GraphChart :nodes="nodes" :links="links" layoutType="force" >
</GraphChart>
</template>
`}}
</pre>
<div class="chart-box">
<GraphChart :nodes="nodes" :links="links">
</GraphChart>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { ref } from 'vue'
import GraphChart from '@/components/base/GraphChart/index.vue'
import CompanyImg from "./symbol.png"
const span = 24
const nodes = ref([
{
id: 1,
name: "泰丰先行",
// category: 0,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
},
{
id: 2,
name: "国轩高科",
// category: 0,
symbolSize: 30,
value: 9,
symbol: `image://${CompanyImg}`,
},
{
id: 3,
name: "智方纳米",
// category: 2,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
},
{
id: 4,
name: "香百科技",
// category: 1,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
},
{
id: 5,
name: "格林滨",
// category: 2,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
},
{
id: 6,
name: "江西紫宸",
// category: 2,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
},
{
id: 7,
name: "紫江企业",
// category: 4,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
},
{
id: 8,
name: "大而美法案",
// category: 4,
symbolSize: 50,
value: 5,
symbol: `image://${CompanyImg}`,
},
{
id: 9,
name: "比亚迪",
// category: 0,
symbolSize: 30,
value: 10,
symbol: `image://${CompanyImg}`,
},
{
id: 10,
name: "铜陵有色",
// category: 3,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
},
{
id: 11,
name: "长盛精密",
// category: 1,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
},
{
id: 12,
name: "天合光能",
// category: 0,
symbolSize: 30,
value: 8,
symbol: `image://${CompanyImg}`,
},
{
id: 13,
name: "昆仑化学",
// category: 2,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
},
{
id: 14,
name: "嘉源科技",
// category: 1,
symbolSize: 30,
value: 6,
symbol: `image://${CompanyImg}`,
},
{
id: 15,
name: "华阳集团",
// category: 4,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
},
{
id: 16,
name: "海辰智能",
// category: 1,
symbolSize: 30,
value: 7,
symbol: `image://${CompanyImg}`,
},
]);
const links = ref([
{ source: 1, target: 7, label: { show: true, formatter: '合作' } },
{ source: 2, target: 7, label: { show: true, formatter: '持股' } },
{ source: 3, target: 7, label: { show: true, formatter: '合作' } },
{ source: 4, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '从属' } },
{ source: 5, target: 7, label: { show: true, formatter: '合作' } },
{ source: 6, target: 7, label: { show: true, formatter: '持股' } },
{ source: 8, target: 7, label: { show: true, formatter: '持股' } },
{ source: 9, target: 7, label: { show: true, formatter: '合作' } },
{ source: 10, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '从属' } },
{ source: 11, target: 7, lineStyle: { type: 'dashed', color: '#d32f2f' }, label: { show: true, formatter: '合作' } },
{ source: 12, target: 7, label: { show: true, formatter: '合作' } },
{ source: 13, target: 7, label: { show: true, formatter: '合作' } },
{ source: 14, target: 7, label: { show: true, formatter: '合作' } },
{ source: 15, target: 7, label: { show: true, formatter: '合作' } },
{ source: 16, target: 7, label: { show: true, formatter: '合作', color: 'red', borderColor: 'red' } },
]);
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
}
.chart-box {
width: 1600px;
height: 800px;
}
</style>
\ No newline at end of file
...@@ -24,6 +24,19 @@ const span = 12 ...@@ -24,6 +24,19 @@ const span = 12
<el-radio-button :value="3">选项3</el-radio-button> <el-radio-button :value="3">选项3</el-radio-button>
</el-radio-group> </el-radio-group>
</el-col> </el-col>
<el-col :span="span">
<pre>{{ `import '@/styles/radio.scss';\n <template>
<el-radio-group class="radio-group-as-radius-btn">
</el-radio-group>
</template>
`}}
</pre>
<el-radio-group v-model="radio" class="radio-group-as-radius-btn">
<el-space>
<el-radio-button v-for="item in 3" :key="item" :value="item">选项{{ item }} ></el-radio-button>
</el-space>
</el-radio-group>
</el-col>
</el-row> </el-row>
</template> </template>
......
...@@ -3,6 +3,7 @@ import { ElSpace, ElRow, ElCol } from 'element-plus'; ...@@ -3,6 +3,7 @@ import { ElSpace, ElRow, ElCol } from 'element-plus';
import '@/styles/tabs.scss' import '@/styles/tabs.scss'
import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue'; import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue';
import AiTipPane from '@/components/base/panes/AiTipPane.vue'; import AiTipPane from '@/components/base/panes/AiTipPane.vue';
import CommonText from '@/components/base/texts/CommonText.vue';
const span = 12 const span = 12
</script> </script>
...@@ -10,20 +11,37 @@ const span = 12 ...@@ -10,20 +11,37 @@ const span = 12
<el-row class="layout-grid-line"> <el-row class="layout-grid-line">
<el-col :span="span"> <el-col :span="span">
<pre> <pre>
{{ `import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue'; {{ `import CommonText from '@/components/base/texts/CommonText.vue';
<template> <template>
<common-text>科技领域</common-text>
<common-text class="text-title-1-show" color="var(--color-yellow-100)">科技领域</common-text>
<common-text color="red" :lineLimit="1">科技领域</common-text>
</template>
`}}
</pre>
<el-space direction="vertical">
<common-text>科技领域</common-text>
<common-text class="text-title-1-show" color="var(--color-yellow-100)">科技领域</common-text>
<common-text color="red"
:lineLimit="1">超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。超出行数则省略号。</common-text>
</el-space>
</el-col>
<el-col :span="span">
<pre>{{ `import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue';\n <template>
<color-prefix-title>科技领域</color-prefix-title> <color-prefix-title>科技领域</color-prefix-title>
<color-prefix-title color="var(--color-yellow-100)">科技领域</color-prefix-title> <color-prefix-title color="var(--color-yellow-100)">科技领域</color-prefix-title>
<color-prefix-title color="red">科技领域</color-prefix-title> <color-prefix-title color="red">科技领域</color-prefix-title>
</template> </template>
`}} `}}
</pre> </pre>
<el-space direction="vertical"> <el-space direction="vertical">
<color-prefix-title>科技领域</color-prefix-title> <color-prefix-title>科技领域</color-prefix-title>
<color-prefix-title color="var(--color-yellow-100)">科技领域</color-prefix-title> <color-prefix-title color="var(--color-yellow-100)">科技领域</color-prefix-title>
<color-prefix-title color="red">科技领域</color-prefix-title> <color-prefix-title color="red">科技领域</color-prefix-title>
</el-space> </el-space>
</el-col> </el-col>
<el-col :span="span"> <el-col :span="span">
<pre>{{ `import AiTipPane from '@/components/base/panes/AiTipPane.vue';\n<template> <pre>{{ `import AiTipPane from '@/components/base/panes/AiTipPane.vue';\n<template>
<ai-tip-pane>huidadadadadasda</ai-tip-pane> <ai-tip-pane>huidadadadadasda</ai-tip-pane>
......
<script setup>
import { ElRow, ElCol } from 'element-plus';
import '@/styles/common.scss'
import WarnningPane from '@/components/base/WarningPane/index.vue'
const span = 12
</script>
<template>
<el-row class="wrapper layout-grid-line">
<el-col :span="span">
<pre>
{{
`import WarnningPane from '@/components/base/WarningPane/index.vue';
<template>
<WarnningPane warnningLevel="特别重大风险" warnningContent="我是特别重大风险内容文字我是特别重大风险内容文字">
</WarnningPane>
</template>
`}}
</pre>
<WarnningPane warnningLevel="特别重大风险" warnningContent="我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字我是特别重大风险内容文字">
</WarnningPane>
</el-col>
</el-row>
</template>
<style lang="scss" scoped>
.person-avatar {
width: 200px;
}
</style>
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<div class="text-title-1-show">文字样式</div> <div class="text-title-1-show">文字样式</div>
<TextStyle /> <TextStyle />
<div class="text-title-1-show">通用样式/组件</div> <div class="text-title-1-show">通用样式/组件</div>
<div style="position: relative; min-height: 300px;"> <div style="position: relative; min-height: 700px;">
<el-tabs tabPosition="left" class="tabs-nav-no-wrap left-float-nav-tabs"> <el-tabs tabPosition="left" class="tabs-nav-no-wrap left-float-nav-tabs">
<el-tab-pane label="通用" lazy> <el-tab-pane label="通用" lazy>
<common-page /> <common-page />
...@@ -28,6 +28,15 @@ ...@@ -28,6 +28,15 @@
<el-tab-pane label="人物" lazy> <el-tab-pane label="人物" lazy>
<people-page /> <people-page />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="预警面板" lazy>
<WarnningPane />
</el-tab-pane>
<el-tab-pane label="层级关系图" lazy>
<GraphChart />
</el-tab-pane>
<el-tab-pane label="引力关系图" lazy>
<GraphTreeChart />
</el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
</el-space> </el-space>
...@@ -47,6 +56,9 @@ import CommonPage from './CommonPage/index.vue'; ...@@ -47,6 +56,9 @@ import CommonPage from './CommonPage/index.vue';
import TextPage from './TextPage/index.vue'; import TextPage from './TextPage/index.vue';
import ImagesPage from './Images/index.vue'; import ImagesPage from './Images/index.vue';
import PeoplePage from './People/index.vue'; import PeoplePage from './People/index.vue';
import WarnningPane from './WarnningPane/index.vue'
import GraphChart from './GraphChart/index.vue'
import GraphTreeChart from './GraphTreeChart/index.vue'
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
......
...@@ -5,4 +5,15 @@ ...@@ -5,4 +5,15 @@
padding: 16px 160px; padding: 16px 160px;
// width: 1600px; // width: 1600px;
align-items: center; align-items: center;
}
//水平居中
.h-center {
//水平居中
margin: 0 auto;
display: block; // 强制设置为块级元素
}
.common-padding {
padding: 20px 24px;
} }
\ No newline at end of file
...@@ -16,4 +16,35 @@ ...@@ -16,4 +16,35 @@
--el-radio-button-checked-border-color: var(--color-primary-5); --el-radio-button-checked-border-color: var(--color-primary-5);
border-radius: 4px; border-radius: 4px;
} }
}
.radio-group-as-radius-btn {
.el-radio-button {
--el-radio-button-checked-bg-color: transparent;
--el-radio-button-checked-border-color: transparent;
.el-radio-button__inner {
border-radius: 32px !important;
// height: 40px;
@extend .text-regular;
background-color: var(--bg-white-65);
border-color: var(--bg-white-100);
border-width: 1px;
color: var(--text-primary-65-color);
//垂直居中
// display: flex;
// justify-content: center;
}
}
.el-radio-button.is-active {
--el-radio-button-checked-text-color: var(--color-primary-100);
.el-radio-button__inner {
background-color: var(--color-primary-10) !important;
border-color: var(--color-primary-35) !important;
}
}
} }
\ No newline at end of file
// 绘制echarts图表 // 绘制echarts图表
import * as echarts from 'echarts' import * as echarts from 'echarts'
import 'echarts-wordcloud';
const setChart = (option, chartId) => { const setChart = (option, chartId) => {
let chartDom = document.getElementById(chartId); let chartDom = document.getElementById(chartId);
if (!chartDom) { if (!chartDom) {
......
...@@ -141,12 +141,30 @@ ...@@ -141,12 +141,30 @@
@page-change="handleNewsPageChange" @page-change="handleNewsPageChange"
/> />
</div> </div>
<!-- <custom-container title="美对华领域打压遏制排行" :titleIcon="icon3" height="700px">
<template #header-right>
<div class="title-right-select">
<el-select
v-model="selectedField"
@change="handleFieldChange"
placeholder="全部领域"
class="field-select"
:style="{ width: '160px' }"
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</template>
</custom-container> -->
<div class="empty-section"> <div class="empty-section">
<div class="bottom-item"> <div class="bottom-item">
<div class="bottom-item-title"> <div class="bottom-item-title">
<img :src="icon3" alt="" /> <img :src="icon3" alt="" />
<span>美对华领域打压遏制排行</span> <span>美对华领域打压遏制排行</span>
</div> </div>
<el-select v-model="selectedField" placeholder="全部领域" class="field-select">
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div> </div>
<div class="select-box"> <div class="select-box">
<div class="rank-btns"> <div class="rank-btns">
...@@ -160,13 +178,18 @@ ...@@ -160,13 +178,18 @@
受打压院校 受打压院校
</div> </div>
</div> </div>
<el-select v-model="selectedField" placeholder="全部领域" class="field-select">
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div> </div>
<div class="main-box" v-loading="rankLoading" element-loading-background="rgba(255, 255, 255, 0.5)"> <div class="main-box" v-loading="rankLoading" element-loading-background="rgba(255, 255, 255, 0.5)">
<!-- 机构排行的原有样式 -->
<template v-if="rankType === 'institution'"> <template v-if="rankType === 'institution'">
<div class="table-header">
<div class="col-rank col-rank-75"></div>
<div class="col-name" style="color: rgb(59, 65, 75); font-weight: 700">部门名称</div>
<div class="col-domain" style="color: rgb(59, 65, 75); font-weight: 700"></div>
<div class="col-date" style="color: rgb(59, 65, 75); font-weight: 700"></div>
<div class="col-member" v-if="rankType !== 'school'" style="color: rgb(59, 65, 75); font-weight: 700">
打压次数
</div>
</div>
<div v-for="(item, index) in rankList" :key="index" class="rank-item"> <div v-for="(item, index) in rankList" :key="index" class="rank-item">
<div class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</div> <div class="rank-num" :class="'rank-' + (index + 1)">{{ index + 1 }}</div>
<img :src="item.orgPicture ? item.orgPicture : defaultImg" alt="" class="rank-icon" /> <img :src="item.orgPicture ? item.orgPicture : defaultImg" alt="" class="rank-icon" />
...@@ -177,7 +200,6 @@ ...@@ -177,7 +200,6 @@
<div class="rank-count">{{ item.count }}</div> <div class="rank-count">{{ item.count }}</div>
</div> </div>
</template> </template>
<!-- 企业/院校排行的表格样式 -->
<template v-else> <template v-else>
<div class="table-header"> <div class="table-header">
<div class="col-rank"></div> <div class="col-rank"></div>
...@@ -210,7 +232,6 @@ ...@@ -210,7 +232,6 @@
</div> </div>
</div> </div>
<div class="col-date">{{ item.date }}</div> <div class="col-date">{{ item.date }}</div>
<!-- <div class="col-member" v-if="rankType !== 'school'">{{ item.member }}</div> -->
<div class="col-member" v-if="rankType !== 'school'">{{ item.province }}</div> <div class="col-member" v-if="rankType !== 'school'">{{ item.province }}</div>
</div> </div>
</div> </div>
...@@ -315,10 +336,13 @@ import getMultiLineChart from "./multiLineChart"; ...@@ -315,10 +336,13 @@ import getMultiLineChart from "./multiLineChart";
import CommonPrompt from "../../../../commonPrompt/index.vue"; import CommonPrompt from "../../../../commonPrompt/index.vue";
import leftBtn from "../../assets/left-btn.png"; import leftBtn from "../../assets/left-btn.png";
import rightBtn from "../../assets/right-btn.png"; import rightBtn from "../../assets/right-btn.png";
import icon1 from "./icon/icon-1.png";
import icon3 from "./icon/icon-3.png"; import icon3 from "./icon/icon-3.png";
import icon4 from "./icon/icon-4.png"; import icon4 from "./icon/icon-4.png";
import defaultImg from "../../../../assets/images/default-icon2.png"; import defaultImg from "../../../../assets/images/default-icon2.png";
import { fieldOptions } from "@/views/ZMOverView/public.js";
import { import {
getAllDomainCount, getAllDomainCount,
getDomainContainmentTrend, getDomainContainmentTrend,
...@@ -330,6 +354,7 @@ import { getUSGovernmentLatestDynamic, getDepartmentList, getSanTypeList } from ...@@ -330,6 +354,7 @@ import { getUSGovernmentLatestDynamic, getDepartmentList, getSanTypeList } from
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue"; import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import SimplePagination from "@/components/SimplePagination.vue"; import SimplePagination from "@/components/SimplePagination.vue";
import CustomContainer from "@/components/Container/index.vue";
const router = useRouter(); const router = useRouter();
...@@ -752,23 +777,23 @@ const svgHeight = computed(() => { ...@@ -752,23 +777,23 @@ const svgHeight = computed(() => {
return startY + rows * rowHeight + 50; return startY + rows * rowHeight + 50;
}); });
const fieldOptions = [ // const fieldOptions = [
{ value: "", label: "全部领域" }, // { value: "", label: "全部领域" },
{ value: "1", label: "人工智能" }, // { value: "1", label: "人工智能" },
{ value: "2", label: "生物科技" }, // { value: "2", label: "生物科技" },
{ value: "3", label: "新一代信息技术" }, // { value: "3", label: "新一代信息技术" },
{ value: "4", label: "量子科技" }, // { value: "4", label: "量子科技" },
{ value: "5", label: "新能源" }, // { value: "5", label: "新能源" },
{ value: "6", label: "集成电路" }, // { value: "6", label: "集成电路" },
{ value: "7", label: "海洋" }, // { value: "7", label: "海洋" },
{ value: "8", label: "先进制造" }, // { value: "8", label: "先进制造" },
{ value: "9", label: "新材料" }, // { value: "9", label: "新材料" },
{ value: "10", label: "航空航天" }, // { value: "10", label: "航空航天" },
{ value: "11", label: "深海" }, // { value: "11", label: "深海" },
{ value: "12", label: "极地" }, // { value: "12", label: "极地" },
{ value: "13", label: "太空" }, // { value: "13", label: "太空" },
{ value: "14", label: "核" } // { value: "14", label: "核" }
]; // ];
// 全领域统计 // 全领域统计
const buttonsData = ref([]); const buttonsData = ref([]);
...@@ -1614,8 +1639,8 @@ watch(activeDate, () => { ...@@ -1614,8 +1639,8 @@ watch(activeDate, () => {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
padding-left: 17px; padding-left: 16px;
padding-right: 35px; padding-right: 16px;
box-sizing: border-box; box-sizing: border-box;
background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%); background: linear-gradient(180deg, rgba(231, 243, 255, 1) 0%, rgba(231, 243, 255, 0) 100%);
.bottom-item-title { .bottom-item-title {
...@@ -1634,14 +1659,19 @@ watch(activeDate, () => { ...@@ -1634,14 +1659,19 @@ watch(activeDate, () => {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
} }
} }
.field-select {
width: 160px;
}
} }
.select-box { .select-box {
width: 691px; width: 691px;
height: 32px; height: 50px;
margin: 10px auto 5px auto; margin: 10px auto 5px auto;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
border-bottom: 1px solid #eee;
padding-bottom: 15px;
.rank-btns { .rank-btns {
display: flex; display: flex;
...@@ -1698,7 +1728,7 @@ watch(activeDate, () => { ...@@ -1698,7 +1728,7 @@ watch(activeDate, () => {
box-sizing: border-box; box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 28px; gap: 21px;
overflow-y: auto; overflow-y: auto;
.rank-item { .rank-item {
display: flex; display: flex;
...@@ -1910,6 +1940,9 @@ watch(activeDate, () => { ...@@ -1910,6 +1940,9 @@ watch(activeDate, () => {
text-align: center; text-align: center;
flex-shrink: 0; flex-shrink: 0;
} }
.col-rank-75 {
width: 75px;
}
.col-name { .col-name {
flex: 1.5; flex: 1.5;
min-width: 0; min-width: 0;
......
<template> <template>
<div class="content-wrapper"> <div class="content-wrapper">
<div class="header"> <div class="header">
<div class="header-arrow-left" @click="prev"> <!-- <div class="header-arrow-left" @click="prev">
<img src="../../assets/left-btn.png" alt="" /> <img src="../../assets/left-btn.png" alt="" />
</div> </div>
<div class="header-arrow-right" @click="next"> <div class="header-arrow-right" @click="next">
<img src="../../assets/right-btn.png" alt="" /> <img src="../../assets/right-btn.png" alt="" />
</div> </div> -->
<div class="cards-mask"> <div class="cards-mask">
<div class="item-box" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }"> <!-- <div class="item-box" :style="{ transform: `translateX(-${currentIndex * (307 + 16)}px)` }"> -->
<div class="item-box">
<div <div
class="header-item" class="header-item"
:class="{ :class="{
...@@ -75,12 +76,60 @@ ...@@ -75,12 +76,60 @@
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<container
:title="'美对华科技要素打压遏制数量趋势'"
:header-bg-color="headerBgColor"
:headerBorderBottom="false"
:title-icon="trendIcon"
height="500px"
>
<template #header-right>
<el-select
v-model="selectField"
@change="handleFieldChange"
placeholder="全部领域"
:style="{ width: '160px' }"
>
<el-option v-for="item in fieldOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
<!-- <el-select
v-model="selectSandMethod"
@change="handleSandMethodChange"
placeholder="全部制裁手段"
:style="{ width: '160px' }"
>
<el-option v-for="item in sanTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> -->
</template>
<el-empty
v-if="usChinaSanctionTrendData.length == 0"
style="padding-top: 120px"
description="暂无数据"
:image-size="100"
/>
<Echarts v-else :option="usChinaSanctionTrendOptions" height="410px"></Echarts>
</container>
<div class="box box2"> <div class="box box2">
<div class="box2-header"> <div class="box2-header">
<div class="icon"> <div class="box2-header-left">
<img src="./assets/images/box-header-icon2.png" alt="" /> <img src="./assets/images/box-header-icon2.png" alt="" />
<div class="title">{{ "美对华要素打压情况" }}</div>
</div>
<div class="box2-header-right">
<el-select
v-model="selectSuppressElement"
@change="handleElementSuppressChange"
placeholder="全部科技要素"
class="field-select"
>
<el-option
v-for="item in elementOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div> </div>
<div class="title">{{ "美对华要素打压情况" }}</div>
</div> </div>
<div class="box2-main"> <div class="box2-main">
<div class="inner-box1"> <div class="inner-box1">
...@@ -88,7 +137,7 @@ ...@@ -88,7 +137,7 @@
<div class="left-main"> <div class="left-main">
<el-empty <el-empty
v-if="box2DataList.length === 0" v-if="box2DataList.length === 0"
style="padding-top: 80px" style="padding-top: 140px"
description="暂无数据" description="暂无数据"
:image-size="100" :image-size="100"
/> />
...@@ -99,12 +148,30 @@ ...@@ -99,12 +148,30 @@
:key="index" :key="index"
@click="handleClickBox2Item(index)" @click="handleClickBox2Item(index)"
> >
<div class="id">{{ index + 1 }}</div> <div class="id">{{ (box2CurrentPage - 1) * box2PageSize + index + 1 }}</div>
<div class="text">{{ item.name }}</div> <div class="left-item-content">
<div class="content-header">
<div class="text">{{ item.name }}</div>
<div class="date">{{ item.postDate }}</div>
</div>
<div class="describe">{{ item.describe || item.name }}</div>
<div class="content-footer">
<div class="element-box">
<AreaTag v-for="(ele, idxx) in item.elementList" :key="idxx" :tagName="ele" />
</div>
<div class="tag-box">
<AreaTag
v-for="(ele, idxx) in item.domainList"
:key="idxx"
:tagName="ele.industryName"
/>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="left-footer" v-if="box2DataList.length !== 0"> <div class="left-footer" v-if="box2AllDataList.length !== 0">
<el-pagination <!-- <el-pagination
background background
layout="prev, pager, next" layout="prev, pager, next"
:total="box2Total" :total="box2Total"
...@@ -113,13 +180,19 @@ ...@@ -113,13 +180,19 @@
@current-change="handleGetBox2DataList" @current-change="handleGetBox2DataList"
size="small" size="small"
:pager-count="4" :pager-count="4"
/> -->
<simple-pagination
v-model:current-page="box2CurrentPage"
:page-size="box2PageSize"
:total="box2AllDataList.length"
@page-change="handleBox2PageChange"
/> />
</div> </div>
</div> </div>
<div class="right" @click="handleToDecreeDetail(box2DetailInfo)"> <div class="right" @click="handleToDecreeDetail(box2DetailInfo)">
<el-empty <el-empty
v-if="box2DataList.length === 0 || !isShowBox2Info" v-if="box2DataList.length === 0 || !isShowBox2Info"
style="padding-top: 80px" style="padding-top: 140px"
description="暂无数据" description="暂无数据"
:image-size="100" :image-size="100"
/> />
...@@ -154,6 +227,12 @@ ...@@ -154,6 +227,12 @@
</div> </div>
</div> </div>
<div class="inner-box2"> <div class="inner-box2">
<el-empty
v-if="box2ChartData.length === 0"
style="padding-top: 140px"
description="暂无数据"
:image-size="100"
/>
<div class="chart-header">{{ "关键词云" }}</div> <div class="chart-header">{{ "关键词云" }}</div>
<div class="box2Chart" id="box2Chart"></div> <div class="box2Chart" id="box2Chart"></div>
</div> </div>
...@@ -161,10 +240,25 @@ ...@@ -161,10 +240,25 @@
</div> </div>
<div class="box box3"> <div class="box box3">
<div class="box3-header"> <div class="box3-header">
<div class="icon"> <div class="box3-header-left">
<img src="./assets/images/box-header-icon2.png" alt="" /> <img src="./assets/images/box-header-icon2.png" alt="" />
<div class="title">{{ "美自身要素发展情况" }}</div>
</div>
<div class="box3-header-right">
<el-select
v-model="selectDevElement"
@change="handleElementDevChange"
placeholder="全部科技要素"
class="field-select"
>
<el-option
v-for="item in elementOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div> </div>
<div class="title">{{ "美自身要素发展情况" }}</div>
</div> </div>
<div class="box3-main"> <div class="box3-main">
<div class="inner-box1"> <div class="inner-box1">
...@@ -172,7 +266,7 @@ ...@@ -172,7 +266,7 @@
<div class="left-main"> <div class="left-main">
<el-empty <el-empty
v-if="box3DataList.length === 0" v-if="box3DataList.length === 0"
style="padding-top: 80px" style="padding-top: 140px"
description="暂无数据" description="暂无数据"
:image-size="100" :image-size="100"
/> />
...@@ -183,12 +277,30 @@ ...@@ -183,12 +277,30 @@
:key="index" :key="index"
@click="handleClickBox3Item(index)" @click="handleClickBox3Item(index)"
> >
<div class="id">{{ index + 1 }}</div> <div class="id">{{ (box3CurrentPage - 1) * box3PageSize + index + 1 }}</div>
<div class="text">{{ item.name }}</div> <div class="left-item-content">
<div class="content-header">
<div class="text">{{ item.name }}</div>
<div class="date">{{ item.postDate }}</div>
</div>
<div class="describe">{{ item.describe || item.name }}</div>
<div class="content-footer">
<div class="element-box">
<AreaTag v-for="(ele, idxx) in item.elementList" :key="idxx" :tagName="ele" />
</div>
<div class="tag-box">
<AreaTag
v-for="(ele, idxx) in item.domainList"
:key="idxx"
:tagName="ele.industryName"
/>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="left-footer" v-if="box3DataList.length"> <div class="left-footer" v-if="box3AllDataList.length !== 0">
<el-pagination <!-- <el-pagination
background background
layout="prev, pager, next" layout="prev, pager, next"
:total="box3Total" :total="box3Total"
...@@ -197,13 +309,19 @@ ...@@ -197,13 +309,19 @@
@current-change="handleGetBox3DataList" @current-change="handleGetBox3DataList"
size="small" size="small"
:pager-count="4" :pager-count="4"
/> -->
<simple-pagination
v-model:current-page="box3CurrentPage"
:page-size="box3PageSize"
:total="box3AllDataList.length"
@page-change="handleBox3PageChange"
/> />
</div> </div>
</div> </div>
<div class="right" @click="handleToDecreeDetail(box3DetailInfo)"> <div class="right" @click="handleToDecreeDetail(box3DetailInfo)">
<el-empty <el-empty
v-if="box3DataList.length === 0 || !isShowBox3Info" v-if="box3DataList.length === 0 || !isShowBox3Info"
style="padding-top: 80px" style="padding-top: 140px"
description="暂无数据" description="暂无数据"
:image-size="100" :image-size="100"
/> />
...@@ -240,13 +358,12 @@ ...@@ -240,13 +358,12 @@
<div class="inner-box2"> <div class="inner-box2">
<el-empty <el-empty
v-if="box3ChartData.length === 0" v-if="box3ChartData.length === 0"
style="padding-top: 80px" style="padding-top: 140px"
description="暂无数据" description="暂无数据"
:image-size="100" :image-size="100"
/> />
<div class="chart-header">{{ "关键词云" }}</div> <div class="chart-header">{{ "关键词云" }}</div>
<div class="box3Chart" id="box3Chart"></div> <div class="box3Chart" id="box3Chart"></div>
<!-- <Echarts :option="usChinaSanctionTrendOptions" height="100%"></Echarts> -->
</div> </div>
</div> </div>
</div> </div>
...@@ -256,7 +373,7 @@ ...@@ -256,7 +373,7 @@
</template> </template>
<script setup> <script setup>
import { onMounted, ref, computed, inject, watch, onUnmounted } from "vue"; import { onMounted, ref, computed, inject, watch, onUnmounted, nextTick } from "vue";
import router from "@/router"; import router from "@/router";
import setChart from "@/utils/setChart"; import setChart from "@/utils/setChart";
import getWordCloudChart from "./uitls/worldCloudChart"; import getWordCloudChart from "./uitls/worldCloudChart";
...@@ -268,9 +385,18 @@ import { ...@@ -268,9 +385,18 @@ import {
getKeyWordUp, getKeyWordUp,
getElementDevelop, getElementDevelop,
getKeyWordDown, getKeyWordDown,
getOrderInfo getOrderInfo,
getElementSuppressTrend
} from "@/api/zmOverview/allElement"; } from "@/api/zmOverview/allElement";
import Echarts from "@/components/Chart/index.vue"; import Echarts from "@/components/Chart/index.vue";
import { elementOptions, sanTypeOptions, fieldOptions } from "@/views/ZMOverView/public.js";
import SimplePagination from "@/components/SimplePagination.vue";
import container from "@/views/ZMOverView/components/fourSuppress/components/container/index.vue";
import trendIcon from "./assets/images/icon-trend.png";
import { Legend } from "@antv/g6";
const headerBgColor = ref("linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%)");
const activeDate = inject("activeDate"); const activeDate = inject("activeDate");
...@@ -328,23 +454,189 @@ const handleGetHeaderList = async () => { ...@@ -328,23 +454,189 @@ const handleGetHeaderList = async () => {
} catch (error) {} } catch (error) {}
}; };
// 数量趋势
const selectField = ref("");
const selectSandMethod = ref("");
const handleFieldChange = val => {
getUsChinaSanctionTrendData();
};
const handleSandMethodChange = val => {};
const getChartOption = (xAxisData = [], seriesData = []) => {
const option = {
tooltip: {
trigger: "axis",
backgroundColor: "rgba(255, 255, 255, 0.9)",
borderColor: "#2f79c4",
textStyle: {
color: "#666"
},
formatter: function (params) {
console.log("图形数据的详情", params);
if (!params || params.length === 0) return "";
let result = `<div style="padding: 6px 10px;">
<div style="font-weight: bold; margin-bottom: 6px; padding-bottom: 4px; border-bottom: 1px solid #eee;">${params[0].name}</div>`;
params.forEach(param => {
const color = param.borderColor || "#2f79c4";
result += `
<div style="display: flex; align-items: center; margin-top: 4px;">
<span style="display: inline-block; width: 10px; height: 10px; border-radius: 50%; background-color: ${color}; margin-right: 8px;"></span>
<span style="color: #666;">${param.seriesName}:</span>
<span style="color: ${color}; font-weight: bold; margin-left: 8px;">${param.value}</span>
</div>`;
});
result += `</div>`;
return result;
}
},
legend: {
show: true,
top: "10px",
// left: "center",
itemWidth: 15,
itemHeight: 10,
itemGap: 20,
textStyle: {
color: "#666",
fontSize: 14
}
},
grid: {
top: "10%",
left: "1%",
right: "2%",
bottom: "5%",
containLabel: true
},
xAxis: {
type: "category",
boundaryGap: false,
data: xAxisData,
axisLine: {
lineStyle: {
color: "#f0f0f0"
}
},
axisLabel: {
color: "#999",
fontSize: 12
},
axisTick: {
show: false
}
},
yAxis: {
name: "次数",
fontSize: 18,
type: "value",
min: 0,
minInterval: 1, // 保证最小间隔为1,避免小数
axisLine: {
show: false
},
axisLabel: {
color: "#999",
fontSize: 12
},
splitLine: {
lineStyle: {
type: "dashed",
color: "#f0f0f0"
}
}
},
series: seriesData.map(item => {
return {
name: item.name,
data: item.data,
type: "line",
// smooth: true,
symbol: "circle",
symbolSize: 8,
itemStyle: {
color: item.color || "#fff",
borderColor: item.borderColor || "#2f79c4",
borderWidth: 2
},
lineStyle: {
color: item.borderColor || "#2f79c4",
width: 2
}
};
})
};
return option;
};
const usChinaSanctionTrendOptions = ref(getChartOption([], []));
const usChinaSanctionTrendData = ref([]);
// 获取打压遏制趋势数据
const getUsChinaSanctionTrendData = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = {
date: startDate,
domainId: selectField.value
// sanTypeId: selectSandMethod.value
};
try {
const res = await getElementSuppressTrend(params);
console.log("美对华科技要素打压遏制数量趋势", res);
if (res.code === 200 && res.data) {
// 按 elementName 分组整理数据
const dataMap = new Map();
const dateSet = new Set();
usChinaSanctionTrendData.value = res.data;
res.data.forEach(item => {
const { elementName, elementDate, elementNum } = item;
dateSet.add(elementDate);
if (!dataMap.has(elementName)) {
dataMap.set(elementName, []);
}
dataMap.get(elementName).push({
date: elementDate,
value: elementNum
});
});
// 排序日期
const sortedDates = Array.from(dateSet).sort();
// 为每个 elementName 生成完整的数据系列(补齐缺失的日期)
const seriesData = [];
const colors = ["#2f79c4", "#e74c3c", "#2ecc71", "#f39c12", "#9b59b6", "#1abc9c"];
dataMap.forEach((values, elementName) => {
// 创建日期到值的映射
const valueMap = new Map(values.map(v => [v.date, v.value]));
// 补齐所有日期的数据
const seriesValues = sortedDates.map(date => valueMap.get(date) || 0);
seriesData.push({
name: elementName,
data: seriesValues,
borderColor: colors[seriesData.length % colors.length]
});
});
usChinaSanctionTrendOptions.value = getChartOption(sortedDates, seriesData);
console.log("美对华科技要素打压遏制数量趋势 options", usChinaSanctionTrendOptions.value);
}
} catch (error) {
console.error("获取打压遏制趋势数据失败:", error);
usChinaSanctionTrendOptions.value = getChartOption([], []);
usChinaSanctionTrendData.value = [];
}
};
// 最新动态 // 最新动态
const box1DataList = ref([ const box1DataList = ref([]);
// {
// title: '更新"中国涉军企业清单"(NS-CMIC List)',
// content: "国防部新增多家中国高科技实体至清单,依据法律禁止美国人士对其进行投资。",
// tagList: ["科研机构", "科研经费"],
// time: "2025年12月18日",
// areaList: [
// {
// name: "量子科技"
// },
// {
// name: "量子科技"
// }
// ]
// }
]);
const handleGetBox1Data = async () => { const handleGetBox1Data = async () => {
try { try {
const res = await getNewDynamics(); const res = await getNewDynamics();
...@@ -355,7 +647,16 @@ const handleGetBox1Data = async () => { ...@@ -355,7 +647,16 @@ const handleGetBox1Data = async () => {
} catch (error) {} } catch (error) {}
}; };
const selectSuppressElement = ref("");
const handleElementSuppressChange = val => {
selectSuppressElement.value = val;
box2CurrentPage.value = 1;
handleGetBox2DataList();
handleBox2Chart();
};
const box2DataList = ref([]); const box2DataList = ref([]);
const box2AllDataList = ref([]);
const box2Total = ref(0); const box2Total = ref(0);
const box2CurrentPage = ref(1); const box2CurrentPage = ref(1);
const box2PageSize = ref(5); const box2PageSize = ref(5);
...@@ -384,20 +685,32 @@ const handleGetBox2DataList = async () => { ...@@ -384,20 +685,32 @@ const handleGetBox2DataList = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value); const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = { const params = {
currentPage: box2CurrentPage.value, currentPage: box2CurrentPage.value,
pageSize: box2PageSize.value, pageSize: 100,
date: startDate date: startDate,
elementId: selectSuppressElement.value
}; };
try { try {
const res = await getElementSuppress(params); const res = await getElementSuppress(params);
console.log("美对我要素打压情况", res); console.log("美对我要素打压情况", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
box2DataList.value = res.data.content; box2AllDataList.value = res.data.content;
box2Total.value = res.data.totalElements; box2Total.value = res.data.totalElements;
handleGetOrderInfo(box2DataList.value[box2LeftActiveIndex.value].id); // handleGetOrderInfo(box2DataList.value[box2LeftActiveIndex.value].id);
} }
updateBox2DataList();
} catch (error) {} } catch (error) {}
}; };
const updateBox2DataList = () => {
const start = (box2CurrentPage.value - 1) * box2PageSize.value;
const end = start + box2PageSize.value;
box2DataList.value = box2AllDataList.value.slice(start, end);
};
const handleBox2PageChange = page => {
updateBox2DataList();
};
const handleClickBox2Item = index => { const handleClickBox2Item = index => {
box2LeftActiveIndex.value = index; box2LeftActiveIndex.value = index;
handleGetOrderInfo(box2DataList.value[index].id); handleGetOrderInfo(box2DataList.value[index].id);
...@@ -407,7 +720,8 @@ const box2ChartData = ref([]); ...@@ -407,7 +720,8 @@ const box2ChartData = ref([]);
const handleGetBox2ChartData = async () => { const handleGetBox2ChartData = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value); const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = { const params = {
date: startDate date: startDate,
elementId: selectSuppressElement.value
}; };
try { try {
const res = await getKeyWordUp(params); const res = await getKeyWordUp(params);
...@@ -431,14 +745,26 @@ const handleBox2Chart = async () => { ...@@ -431,14 +745,26 @@ const handleBox2Chart = async () => {
setChart(box2Chart, "box2Chart"); setChart(box2Chart, "box2Chart");
}; };
const box3DataList = ref([]); const selectDevElement = ref("");
const handleElementDevChange = val => {
selectDevElement.value = val;
box3CurrentPage.value = 1;
handleGetBox3DataList();
handleBox3Chart();
};
const box3DataList = ref([]);
const box3AllDataList = ref([]);
const box3Total = ref(0); const box3Total = ref(0);
const box3CurrentPage = ref(1); const box3CurrentPage = ref(1);
const box3PageSize = ref(5); const box3PageSize = ref(5);
const box3LeftActiveIndex = ref(0); const box3LeftActiveIndex = ref(0);
const box3DetailInfo = ref({}); const box3DetailInfo = ref({});
const handleBox3PageChange = page => {
updateBox3DataList();
};
// 根据id 获取政令详细信息 // 根据id 获取政令详细信息
const handleGetOrderInfo1 = async id => { const handleGetOrderInfo1 = async id => {
const params = { const params = {
...@@ -462,26 +788,34 @@ const handleGetBox3DataList = async () => { ...@@ -462,26 +788,34 @@ const handleGetBox3DataList = async () => {
const params = { const params = {
currentPage: box3CurrentPage.value, currentPage: box3CurrentPage.value,
pageSize: box3PageSize.value, pageSize: 100,
date: startDate date: startDate,
elementId: selectDevElement.value
}; };
try { try {
const res = await getElementDevelop(params); const res = await getElementDevelop(params);
console.log("美自身要素发展情况", res); console.log("美自身要素发展情况", res);
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
box3DataList.value = res.data.content; box3AllDataList.value = res.data.content;
box3Total.value = res.data.totalElements; box3Total.value = res.data.totalElements;
if (res.data.content.length) { // if (res.data.content.length) {
handleGetOrderInfo1(box3DataList.value[box3LeftActiveIndex.value].id); // handleGetOrderInfo1(box3DataList.value[box3LeftActiveIndex.value].id);
} else { // } else {
box3DetailInfo.value = {}; // box3DetailInfo.value = {};
} // }
} else { } else {
box3DetailInfo.value = {}; box3DetailInfo.value = {};
} }
updateBox3DataList();
} catch (error) {} } catch (error) {}
}; };
const updateBox3DataList = async () => {
const start = (box3CurrentPage.value - 1) * box3PageSize.value;
const end = start + box3PageSize.value;
box3DataList.value = box3AllDataList.value.slice(start, end);
};
const handleClickBox3Item = index => { const handleClickBox3Item = index => {
box3LeftActiveIndex.value = index; box3LeftActiveIndex.value = index;
handleGetOrderInfo1(box3DataList.value[index].id); handleGetOrderInfo1(box3DataList.value[index].id);
...@@ -492,7 +826,8 @@ const box3ChartData = ref([]); ...@@ -492,7 +826,8 @@ const box3ChartData = ref([]);
const handleGetBox3ChartData = async () => { const handleGetBox3ChartData = async () => {
const { startDate, endDate } = getCalculatedDate(activeDate.value); const { startDate, endDate } = getCalculatedDate(activeDate.value);
const params = { const params = {
date: startDate date: startDate,
elementId: selectDevElement.value
}; };
try { try {
const res = await getKeyWordDown(params); const res = await getKeyWordDown(params);
...@@ -567,10 +902,13 @@ const handleToDecreeDetail = item => { ...@@ -567,10 +902,13 @@ const handleToDecreeDetail = item => {
}; };
watch(activeDate, async () => { watch(activeDate, async () => {
box2CurrentPage.value = 1;
box3CurrentPage.value = 1;
handleGetBox2DataList(); // 美对我要素打压情况 handleGetBox2DataList(); // 美对我要素打压情况
handleGetBox3DataList(); // 美自身要素发展情况 handleGetBox3DataList(); // 美自身要素发展情况
handleBox2Chart(); // 关键词云-上 handleBox2Chart(); // 关键词云-上
handleBox3Chart(); // 关键词云-下 handleBox3Chart(); // 关键词云-下
getUsChinaSanctionTrendData();
await handleGetHeaderList(); // 全要素统计 await handleGetHeaderList(); // 全要素统计
// startAutoPlay(); // startAutoPlay();
}); });
...@@ -581,6 +919,7 @@ onMounted(async () => { ...@@ -581,6 +919,7 @@ onMounted(async () => {
handleGetBox3DataList(); // 美自身要素发展情况 handleGetBox3DataList(); // 美自身要素发展情况
handleBox2Chart(); // 关键词云-上 handleBox2Chart(); // 关键词云-上
handleBox3Chart(); // 关键词云-下 handleBox3Chart(); // 关键词云-下
getUsChinaSanctionTrendData();
await handleGetHeaderList(); // 全要素统计 await handleGetHeaderList(); // 全要素统计
// startAutoPlay(); // startAutoPlay();
}); });
...@@ -928,7 +1267,32 @@ onUnmounted(() => { ...@@ -928,7 +1267,32 @@ onUnmounted(() => {
border-bottom: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1);
background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%); background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%);
display: flex; display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
.box2-header-left {
display: flex;
align-items: center;
img {
width: 17px;
height: 17px;
margin-right: 8px;
}
.title {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 31px;
letter-spacing: 0px;
text-align: left;
color: rgba(5, 95, 194, 1);
}
}
.field-select {
width: 160px;
}
.icon { .icon {
width: 17px; width: 17px;
height: 16.5px; height: 16.5px;
...@@ -942,8 +1306,8 @@ onUnmounted(() => { ...@@ -942,8 +1306,8 @@ onUnmounted(() => {
} }
.title { .title {
margin-top: 8px; // margin-top: 8px;
margin-left: 13px; // margin-left: 13px;
width: 199px; width: 199px;
height: 31px; height: 31px;
color: var(--color-main-active); color: var(--color-main-active);
...@@ -986,11 +1350,82 @@ onUnmounted(() => { ...@@ -986,11 +1350,82 @@ onUnmounted(() => {
.left-item { .left-item {
width: 100%; width: 100%;
height: 54px; height: 120px;
border: 1px solid transparent; border: 1px solid transparent;
display: flex; display: flex;
align-items: center; padding: 10px;
box-sizing: border-box;
border-bottom: 1px solid #eee;
// align-items: center;
.left-item-content {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 5px;
width: 100%;
.content-header {
display: flex;
justify-content: space-between;
.text {
max-width: 80%;
height: 30px;
margin-left: 12px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.date {
font-family: Source Han Sans CN;
font-size: 16px;
font-weight: 400;
}
}
.describe {
max-width: 80%;
height: 30px;
margin-left: 12px;
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;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.content-footer {
display: flex;
justify-content: space-between;
align-items: center;
.element-box {
display: flex;
flex-direction: row;
gap: 5px;
}
.tag-box {
min-width: 75px;
height: 24px;
gap: 5px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: right;
align-items: center;
}
}
}
&:hover { &:hover {
background: rgba(246, 250, 255, 1); background: rgba(246, 250, 255, 1);
} }
...@@ -998,7 +1433,8 @@ onUnmounted(() => { ...@@ -998,7 +1433,8 @@ onUnmounted(() => {
cursor: pointer; cursor: pointer;
.id { .id {
margin-left: 16px; // margin-left: 16px;
margin-top: 3px;
width: 24px; width: 24px;
height: 24px; height: 24px;
border-radius: 50px; border-radius: 50px;
...@@ -1211,7 +1647,32 @@ onUnmounted(() => { ...@@ -1211,7 +1647,32 @@ onUnmounted(() => {
border-bottom: 1px solid rgba(255, 255, 255, 1); border-bottom: 1px solid rgba(255, 255, 255, 1);
background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%); background: linear-gradient(180deg, rgba(231, 243, 255, 0.5), rgba(231, 243, 255, 0) 100%);
display: flex; display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
.box3-header-left {
display: flex;
align-items: center;
img {
width: 17px;
height: 17px;
margin-right: 8px;
}
.title {
font-family: YouSheBiaoTiHei;
font-size: 24px;
font-weight: 400;
line-height: 31px;
letter-spacing: 0px;
text-align: left;
color: rgba(5, 95, 194, 1);
}
}
.field-select {
width: 160px;
}
.icon { .icon {
width: 17px; width: 17px;
height: 16.5px; height: 16.5px;
...@@ -1225,7 +1686,7 @@ onUnmounted(() => { ...@@ -1225,7 +1686,7 @@ onUnmounted(() => {
} }
.title { .title {
margin-top: 8px; // margin-top: 8px;
margin-left: 13px; margin-left: 13px;
width: 199px; width: 199px;
height: 31px; height: 31px;
...@@ -1267,10 +1728,80 @@ onUnmounted(() => { ...@@ -1267,10 +1728,80 @@ onUnmounted(() => {
.left-item { .left-item {
width: 100%; width: 100%;
height: 54px; height: 120px;
border: 1px solid transparent; border: 1px solid transparent;
display: flex; display: flex;
align-items: center; border-bottom: 1px solid #eee;
padding: 10px;
.left-item-content {
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 5px;
width: 100%;
.content-header {
display: flex;
justify-content: space-between;
.text {
max-width: 80%;
height: 30px;
margin-left: 12px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 16px;
font-weight: 700;
line-height: 30px;
letter-spacing: 0px;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.date {
font-family: Source Han Sans CN;
font-size: 16px;
font-weight: 400;
}
}
.describe {
max-width: 80%;
height: 30px;
margin-left: 12px;
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;
text-align: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.content-footer {
display: flex;
justify-content: space-between;
align-items: center;
.element-box {
display: flex;
flex-direction: row;
gap: 5px;
}
.tag-box {
min-width: 75px;
height: 24px;
gap: 5px;
/* 自动布局 */
display: flex;
flex-direction: row;
justify-content: right;
align-items: center;
}
}
}
&:hover { &:hover {
background: rgba(246, 250, 255, 1); background: rgba(246, 250, 255, 1);
...@@ -1279,7 +1810,9 @@ onUnmounted(() => { ...@@ -1279,7 +1810,9 @@ onUnmounted(() => {
cursor: pointer; cursor: pointer;
.id { .id {
margin-left: 16px; // margin-left: 16px;
margin-top: 3px;
width: 24px; width: 24px;
height: 24px; height: 24px;
border-radius: 50px; border-radius: 50px;
......
{
"code": 200,
"message": "操作成功",
"success": true,
"data": [
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-01"
},
{
"elementName": "科研人才",
"elementNum": 1,
"elementDate": "2025-01"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-01"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-02"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-02"
},
{
"elementName": "科研仪器",
"elementNum": 1,
"elementDate": "2025-02"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-03"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-03"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-03"
},
{
"elementName": "科研机构",
"elementNum": 1,
"elementDate": "2025-04"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-04"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-04"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-05"
},
{
"elementName": "科研人才",
"elementNum": 1,
"elementDate": "2025-05"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-05"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-06"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-06"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-06"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-07"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-07"
},
{
"elementName": "科研仪器",
"elementNum": 3,
"elementDate": "2025-07"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-08"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-08"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-08"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-09"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-09"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-09"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-10"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-10"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-10"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-11"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-11"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-11"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2025-12"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2025-12"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2025-12"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2026-01"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2026-01"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2026-01"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2026-02"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2026-02"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2026-02"
},
{
"elementName": "科研机构",
"elementNum": 0,
"elementDate": "2026-03"
},
{
"elementName": "科研人才",
"elementNum": 0,
"elementDate": "2026-03"
},
{
"elementName": "科研仪器",
"elementNum": 0,
"elementDate": "2026-03"
}
]
}
\ No newline at end of file
No preview for this file type
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论