提交 4090962e authored 作者: yanpeng's avatar yanpeng

Merge branch 'master' into yp-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
......
<svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52.000000" height="52.000000" fill="none" customFrame="#000000">
<defs>
<g id="pixso_custom_effect_0">
<effect x="0.000000" y="0.000000" visibility="visible" fill="rgb(0,0,0)" fill-opacity="0.100000001" effectType="dropShadow" stdDeviation="8" radius="0" />
</g>
<filter id="filter_0" width="52.000000" height="52.000000" x="0.000000" y="0.000000" filterUnits="userSpaceOnUse" customEffect="url(#pixso_custom_effect_0)" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feOffset dx="0.000000" dy="0.000000" in="SourceAlpha" />
<feGaussianBlur stdDeviation="2.66666675" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0 " />
<feBlend result="effect_dropShadow_1" in2="BackgroundImageFix" mode="normal" />
<feBlend result="shape" in="SourceGraphic" in2="effect_dropShadow_1" mode="normal" />
</filter>
<clipPath id="clipPath_0">
<rect width="16.000000" height="16.000000" x="18.000000" y="18.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="普通企业节点" width="36.000000" height="36.000000" x="8.000000" y="8.000000" />
<g filter="url(#filter_0)">
<circle id="椭圆 25" cx="26" cy="26" r="18" fill="rgb(246,250,255)" />
<circle id="椭圆 25" cx="26" cy="26" r="17.5" stroke="rgb(231,243,255)" stroke-width="1.000000" />
</g>
<g id="企业 " clip-path="url(#clipPath_0)" customFrame="url(#clipPath_0)">
<rect id="企业 " width="16.000000" height="16.000000" x="18.000000" y="18.000000" />
<path id="矢量 370" d="M33.4667 31.9231L32.9333 31.9231L32.9333 24.3846C32.9333 23.2 31.9733 22.2308 30.8 22.2308L27.6 22.2308L27.6 21.1538C27.6 19.9692 26.64 19 25.4667 19L21.2 19C20.0267 19 19.0667 19.9692 19.0667 21.1538L19.0667 31.9231L18.5333 31.9231C18.2347 31.9231 18 32.16 18 32.4615C18 32.7631 18.2347 33 18.5333 33L33.4667 33C33.7653 33 34 32.7631 34 32.4615C34 32.16 33.7653 31.9231 33.4667 31.9231ZM26.5333 22.2308L26.5333 23.3077L26.5333 31.9231L20.1333 31.9231L20.1333 21.1538C20.1333 20.5615 20.6133 20.0769 21.2 20.0769L25.4667 20.0769C26.0533 20.0769 26.5333 20.5615 26.5333 21.1538L26.5333 22.2308ZM31.8667 31.9231L27.6 31.9231L27.6 23.3077L30.8 23.3077C31.3867 23.3077 31.8667 23.7923 31.8667 24.3846L31.8667 31.9231ZM24.9333 22.2308L21.7333 22.2308C21.4347 22.2308 21.2 22.4677 21.2 22.7692C21.2 23.0708 21.4347 23.3077 21.7333 23.3077L24.9333 23.3077C25.232 23.3077 25.4667 23.0708 25.4667 22.7692C25.4667 22.4677 25.232 22.2308 24.9333 22.2308ZM24.9333 25.4615L21.7333 25.4615C21.4347 25.4615 21.2 25.6985 21.2 26C21.2 26.3015 21.4347 26.5385 21.7333 26.5385L24.9333 26.5385C25.232 26.5385 25.4667 26.3015 25.4667 26C25.4667 25.6985 25.232 25.4615 24.9333 25.4615ZM24.9333 28.6923L21.7333 28.6923C21.4347 28.6923 21.2 28.9292 21.2 29.2308C21.2 29.5323 21.4347 29.7692 21.7333 29.7692L24.9333 29.7692C25.232 29.7692 25.4667 29.5323 25.4667 29.2308C25.4667 28.9292 25.232 28.6923 24.9333 28.6923ZM30.2667 25.4615L29.2 25.4615C28.9013 25.4615 28.6667 25.6985 28.6667 26C28.6667 26.3015 28.9013 26.5385 29.2 26.5385L30.2667 26.5385C30.5653 26.5385 30.8 26.3015 30.8 26C30.8 25.6985 30.5653 25.4615 30.2667 25.4615ZM30.2667 28.6923L29.2 28.6923C28.9013 28.6923 28.6667 28.9292 28.6667 29.2308C28.6667 29.5323 28.9013 29.7692 29.2 29.7692L30.2667 29.7692C30.5653 29.7692 30.8 29.5323 30.8 29.2308C30.8 28.9292 30.5653 28.6923 30.2667 28.6923Z" fill="rgb(5,95,194)" fill-rule="nonzero" />
</g>
</svg>
<svg viewBox="0 0 52 52" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="52.000000" height="52.000000" fill="none" customFrame="#000000">
<defs>
<g id="pixso_custom_effect_1">
<effect x="0.000000" y="0.000000" visibility="visible" fill="rgb(0,0,0)" fill-opacity="0.100000001" effectType="dropShadow" stdDeviation="8" radius="0" />
</g>
<filter id="filter_1" width="52.000000" height="52.000000" x="0.000000" y="0.000000" filterUnits="userSpaceOnUse" customEffect="url(#pixso_custom_effect_1)" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feOffset dx="0.000000" dy="0.000000" in="SourceAlpha" />
<feGaussianBlur stdDeviation="2.66666675" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0 " />
<feBlend result="effect_dropShadow_1" in2="BackgroundImageFix" mode="normal" />
<feBlend result="shape" in="SourceGraphic" in2="effect_dropShadow_1" mode="normal" />
</filter>
<clipPath id="clipPath_1">
<rect width="16.000000" height="16.000000" x="18.000000" y="18.000000" fill="rgb(255,255,255)" />
</clipPath>
</defs>
<rect id="普通企业节点" width="36.000000" height="36.000000" x="8.000000" y="8.000000" />
<g filter="url(#filter_1)">
<circle id="椭圆 25" cx="26" cy="26" r="18" fill="rgb(255,255,255)" />
<circle id="椭圆 25" cx="26" cy="26" r="17.5" stroke="rgb(230,231,232)" stroke-width="1.000000" />
</g>
<g id="企业 " clip-path="url(#clipPath_1)" customFrame="url(#clipPath_1)">
<rect id="企业 " width="16.000000" height="16.000000" x="18.000000" y="18.000000" />
<path id="矢量 370" d="M33.4667 31.9231L32.9333 31.9231L32.9333 24.3846C32.9333 23.2 31.9733 22.2308 30.8 22.2308L27.6 22.2308L27.6 21.1538C27.6 19.9692 26.64 19 25.4667 19L21.2 19C20.0267 19 19.0667 19.9692 19.0667 21.1538L19.0667 31.9231L18.5333 31.9231C18.2347 31.9231 18 32.16 18 32.4615C18 32.7631 18.2347 33 18.5333 33L33.4667 33C33.7653 33 34 32.7631 34 32.4615C34 32.16 33.7653 31.9231 33.4667 31.9231ZM26.5333 22.2308L26.5333 23.3077L26.5333 31.9231L20.1333 31.9231L20.1333 21.1538C20.1333 20.5615 20.6133 20.0769 21.2 20.0769L25.4667 20.0769C26.0533 20.0769 26.5333 20.5615 26.5333 21.1538L26.5333 22.2308ZM31.8667 31.9231L27.6 31.9231L27.6 23.3077L30.8 23.3077C31.3867 23.3077 31.8667 23.7923 31.8667 24.3846L31.8667 31.9231ZM24.9333 22.2308L21.7333 22.2308C21.4347 22.2308 21.2 22.4677 21.2 22.7692C21.2 23.0708 21.4347 23.3077 21.7333 23.3077L24.9333 23.3077C25.232 23.3077 25.4667 23.0708 25.4667 22.7692C25.4667 22.4677 25.232 22.2308 24.9333 22.2308ZM24.9333 25.4615L21.7333 25.4615C21.4347 25.4615 21.2 25.6985 21.2 26C21.2 26.3015 21.4347 26.5385 21.7333 26.5385L24.9333 26.5385C25.232 26.5385 25.4667 26.3015 25.4667 26C25.4667 25.6985 25.232 25.4615 24.9333 25.4615ZM24.9333 28.6923L21.7333 28.6923C21.4347 28.6923 21.2 28.9292 21.2 29.2308C21.2 29.5323 21.4347 29.7692 21.7333 29.7692L24.9333 29.7692C25.232 29.7692 25.4667 29.5323 25.4667 29.2308C25.4667 28.9292 25.232 28.6923 24.9333 28.6923ZM30.2667 25.4615L29.2 25.4615C28.9013 25.4615 28.6667 25.6985 28.6667 26C28.6667 26.3015 28.9013 26.5385 29.2 26.5385L30.2667 26.5385C30.5653 26.5385 30.8 26.3015 30.8 26C30.8 25.6985 30.5653 25.4615 30.2667 25.4615ZM30.2667 28.6923L29.2 28.6923C28.9013 28.6923 28.6667 28.9292 28.6667 29.2308C28.6667 29.5323 28.9013 29.7692 29.2 29.7692L30.2667 29.7692C30.5653 29.7692 30.8 29.5323 30.8 29.2308C30.8 28.9292 30.5653 28.6923 30.2667 28.6923Z" fill="rgb(59,65,75)" fill-rule="nonzero" />
</g>
</svg>
...@@ -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({
...@@ -150,3 +170,99 @@ export function getPostMemberList() { ...@@ -150,3 +170,99 @@ export function getPostMemberList() {
url: `/api/BillDict/member`, url: `/api/BillDict/member`,
}) })
} }
/**
* 获取筛选项配置 - 行业列表
* 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
...@@ -99,7 +99,7 @@ export function getCharacterResume(params) { ...@@ -99,7 +99,7 @@ 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,
}) })
} }
...@@ -142,3 +142,61 @@ export function getCharacterRelatedEntity(params) { ...@@ -142,3 +142,61 @@ export function getCharacterRelatedEntity(params) {
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
}) })
} }
...@@ -55,3 +55,10 @@ export function getAreaList() { ...@@ -55,3 +55,10 @@ export function getAreaList() {
url: `/api/commonDict/areaType`, url: `/api/commonDict/areaType`,
}) })
} }
export function getNewsDetail(params) {
return request({
method: 'GET',
url: `/api/news/findById/${params.newsId}`,
params
})
}
...@@ -73,6 +73,15 @@ service.interceptors.response.use( ...@@ -73,6 +73,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)) {
ElMessage({ ElMessage({
......
...@@ -165,3 +165,10 @@ export function getRelevantMeasures(params) { ...@@ -165,3 +165,10 @@ export function getRelevantMeasures(params) {
// data, // data,
// }) // })
// } // }
export function getRuleOrg(params) {
return request({
method: 'POST',
url: `/api/organization/relate/ruleOrg`, data: params
})
}
\ No newline at end of file
<template>
<div class="news-box">
<div class="box3-header">
<div class="box3-header-left">
<div class="box3-header-icon">
<img src="@/assets/images/box3-header-icon.png" alt="" />
</div>
<div class="box3-header-title">{{ "新闻资讯" }}</div>
<div class="more" @click="handleToMoreNews">{{ "更多 +" }}</div>
</div>
</div>
<div class="box3-main">
<div class="box3-item" v-for="(news, index) in normalizedList" :key="index"
@click="handleClickToNewsDetail(news)">
<div class="left">
<img :src="getProxyUrl(news.newsImage) || defaultImg" alt="" referrerpolicy="no-referrer"
@error="e => (e.target.src = errImg || News1)" />
</div>
<div class="right">
<div class="right-top">
<div class="title">{{ news.newsTitle || news.title }}</div>
<div class="time">
{{ news.newsDate ? news.newsDate.slice(5) : "" }} {{ news.newsOrg ? "-" + news.newsOrg : "" }}
</div>
</div>
<div class="right-footer">{{ news.newsContent || news.description }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from "vue";
import router from "@/router/index";
import News1 from "@/assets/images/news1.png"; // 错误图片
import defaultNew from "@/assets/images/default-icon-news.png"; // 默认图片
let { list, newsList, errImg, defaultImg } = defineProps({
list: {
type: Array,
default: () => []
},
// 兼容历史用法:部分页面使用 newsList 作为入参
newsList: {
type: Array,
default: undefined
},
defaultImg: {
type: String,
default: defaultNew
},
errImg: {
type: String,
default: ''
}
});
// 统一对外渲染数据源:优先使用 list,其次兼容 newsList
const normalizedList = computed(() => {
return (Array.isArray(list) && list.length ? list : newsList) || [];
});
// 处理图片代理
const getProxyUrl = url => {
if (!url) return "";
const urlStr = String(url);
// 排除非 http 开头(相对路径)、已经是代理链接、或者是本地链接
if (
!urlStr.startsWith("http") ||
urlStr.includes("images.weserv.nl") ||
urlStr.includes("localhost") ||
urlStr.includes("127.0.0.1")
) {
return url;
}
// 移除协议头 http:// 或 https://
const cleanUrl = urlStr.replace(/^https?:\/\//i, "");
return `https://images.weserv.nl/?url=${encodeURIComponent(cleanUrl)}`;
};
const handleClickToNewsDetail = news => {
const route = router.resolve({
path: "/newsAnalysis",
query: {
newsId: news.newsId ?? news.id
}
});
window.open(route.href, "_blank");
};
// 查看更多新闻资讯
const handleToMoreNews = () => {
const route = router.resolve("/newsBrief");
window.open(route.href, "_blank");
};
</script>
<style lang="scss" scoped>
.news-box {
width: 792px;
height: 450px;
border-radius: 10px;
box-shadow: 0px 0px 15px 0px rgba(25, 69, 130, 0.2);
background: rgba(255, 255, 255, 1);
.box3-header {
height: 48px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
margin: 0 auto;
display: flex;
justify-content: space-between;
position: relative;
.box3-header-left {
display: flex;
.box3-header-icon {
margin-left: 21px;
margin-top: 16px;
width: 19px;
height: 19px;
img {
width: 100%;
height: 100%;
}
}
.box3-header-title {
margin-top: 11px;
margin-left: 20px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
line-height: 26px;
}
}
.more {
width: 49px;
height: 24px;
position: absolute;
top: 14px;
right: 27px;
color: rgba(20, 89, 187, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
}
.box3-main {
height: 402px;
overflow-y: auto;
overflow-x: hidden;
padding: 6px 0;
.box3-item {
display: flex;
height: 77px;
width: 749px;
margin-left: 21px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
}
.left {
width: 72px;
height: 48px;
margin-top: 15px;
img {
width: 100%;
height: 100%;
border-radius: 4px;
}
}
.right {
width: 657px;
margin-left: 20px;
.right-top {
width: 657px;
display: flex;
justify-content: space-between;
&:hover {
text-decoration: underline;
color: var(--color-main-active);
.title {
color: var(--color-main-active);
}
}
.title {
margin-top: 13px;
width: 500px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 700;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time {
width: 157px;
text-align: right;
height: 22px;
margin-top: 19px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
line-height: 22px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.right-footer {
width: 657px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: Microsoft YaHei;
font-size: 16px;
font-weight: 400;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
</style>
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
<template>
<div class="search-wrapper">
<div class="search-main" :class="{ 'search-main-with-tabs': enableBillTypeSwitch }">
<input v-model="inputValue" :placeholder="placeholder" @keyup.enter="handleSearch" class="search-input" />
<div class="search-btn" @click="handleSearch">
<img src="@/assets/icons/search-icon.png" alt />
搜索
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
searchText: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
})
const emit = defineEmits(['search', 'update:searchText'])
const inputValue = computed({
get: () => props.searchText,
set: (value) => emit('update:searchText', value)
})
const handleSearch = () => {
emit('search')
}
</script>
<style lang="scss" scoped>
.search-wrapper {
width: 960px;
height: 48px;
.search-main {
display: flex;
padding-right: 3px;
align-items: center;
justify-content: space-between;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
width: 960px;
height: 48px;
background-color: rgba(255, 255, 255, 0.65);
border-radius: 10px;
border: 1px solid #fff;
&:hover {
border: 1px solid var(--color-main-active);
}
.search-input {
border: none;
outline: none;
width: 838px;
height: 48px;
background-color: transparent;
font-size: 14px;
padding: 12px 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 22px;
color: rgba(59, 65, 75, 1);
&::placeholder {
color: #a8abb2;
}
}
.search-btn {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 120px;
height: 46px;
margin-right: -3px;
border-radius: 8px;
background-color: rgb(5, 95, 194);
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 22px;
color: #fff;
img {
width: 18px;
height: 18px;
margin-right: 8px;
}
}
}
}
</style>
\ No newline at end of file
<svg viewBox="0 0 8 8" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="8.000000" height="8.000000" fill="none" customFrame="#000000">
<path id="多边形 2" d="M7.4641 6L0.535898 6L4 0L7.4641 6Z" fill="rgb(255,255,255)" fill-rule="evenodd" transform="matrix(0,1,-1,0,8,0)" />
</svg>
<template>
<div class="sider-tabs-wrapper">
<div class="sider-item"
:class="{'sider-item-active': sider.active}"
v-for="(sider, index) in siderList" :key="sider.name || index"
@click="handleClickSiderItem(sider)"
>
<div
class="sider-item-text text-primary-65-clor text-tip-1"
:class="{'sider-item-text-active': sider.active}">{{ sider.name }}</div>
<div class="sider-item-icon" v-show="sider.active">
<img src="./active-icon.svg" alt="" />
</div>
</div>
</div>
</template>
<script setup>
const props = defineProps({
siderList: {
type: Array,
default: () => [
{
name: '分析内容1',
active: true
},
{
name: '分析内容2',
active: false
},
{
name: '分析内容3',
active: false
},
]
},
})
const emits = defineEmits(['clickSiderItem'])
const handleClickSiderItem = (sider) => {
emits('clickSiderItem', sider)
}
</script>
<style lang="scss" scoped>
.sider-tabs-wrapper {
display: flex;
flex-direction: column;
gap: 16px;
.sider-item {
padding: 4px 28px;
border-radius: 16px;
position: relative;
cursor: pointer;
display: flex;
justify-content: center;
.sider-item-icon{
position: absolute;
top: 6px;
right: 16px;
width: 8px;
height: 8px;
img{
width: 100%;
height: 100%;
}
}
}
}
.sider-item-active{
background: var(--color-main-active);
}
.sider-item-text-active {
color: rgb(255,255,255)
}
</style>
\ No newline at end of file
<template>
<div class="tip-wrapper">
<div class="icon">
<img src="./tip-icon.svg" alt="">
</div>
<div class="text text-tip-2 text-primary-50-clor">{{ `数据来源:${dataSource},数据时间:${dataTime}` }}</div>
</div>
</template>
<script setup>
const props = defineProps({
dataSource: {
type: String,
default: '美国国会官网'
},
dataTime: {
type: String,
default: '2023.1至2025.12'
},
})
</script>
<style lang="scss" scoped>
.tip-wrapper{
width: 100%;
display: flex;
gap: 8px;
justify-content: center;
align-items: center;
height: 22px;
.icon{
width: 16px;
height: 16px;
img{
width: 100%;
height: 100%;
}
}
}
</style>
\ No newline at end of file
<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="容器 704" width="16.000000" height="16.000000" x="0.000000" y="0.000000" />
<circle id="椭圆 96" cx="8" cy="8" r="7" fill="rgb(230,231,232)" />
<circle id="椭圆 97" cx="8" cy="4" r="1" fill="rgb(132,136,142)" />
<path id="矩形 241" d="M6.49996 6L8.00028 6.0004C8.55256 6.0004 9.00028 6.44811 9.00028 7.00039L9.00028 10.4992C9.00028 10.7754 9.22408 10.9989 9.50033 10.9992L9.50033 10.9997C9.77657 10.9998 10.0005 11.2236 10.0005 11.4998L10.0003 11.5001C10.0002 11.7765 9.77622 12.0006 9.49978 12.0006L8.00028 12.0004L6.50033 12.0004C6.22423 12.0004 6.00064 11.7767 6.00049 11.5006L6.00021 11.5005C6.00021 11.2243 6.22418 11.0003 6.50037 11.0003L6.50037 11.0006C6.77649 11.0007 7.00042 10.7766 7.00042 10.5005L7.00017 7.50005C7.00017 7.22376 6.77644 7.00047 6.50015 7.00002L6.49946 6.99922C6.22357 6.999 6 6.77565 6 6.49976C6.00011 6.22373 6.22393 6 6.49996 6Z" fill="rgb(132,136,142)" fill-rule="evenodd" />
</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(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 {
......
<template>
<div class="color-svg" v-if="svgContent">
<div v-html="processedSvgContent" class="svg-container"></div>
</div>
<div class="color-svg-loading" v-else>
<div class="loading-spinner"></div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue';
// 组件属性
const props = defineProps({
// SVG地址
svgUrl: {
type: String,
required: true
},
// SVG颜色
color: {
type: String,
default: null
}
,
size: {
type: Number,
default: null
}
});
// 组件事件
const emit = defineEmits(['svg-loaded', 'svg-error']);
// 状态管理
const svgContent = ref<string>('');
const isLoading = ref<boolean>(false);
const error = ref<string>('');
// 处理后的SVG内容
const processedSvgContent = computed(() => {
if (!svgContent.value) return '';
// 替换SVG中的颜色
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}"`;
});
}
if (props.size) {
// 替换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)
return processed;
});
// 加载SVG内容
const loadSvgContent = async () => {
if (!props.svgUrl) return;
isLoading.value = true;
error.value = '';
try {
const response = await fetch(props.svgUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const content = await response.text();
svgContent.value = content;
emit('svg-loaded', content);
} catch (err) {
error.value = err instanceof Error ? err.message : 'Failed to load SVG';
emit('svg-error', error.value);
console.error('Error loading SVG:', err);
} finally {
isLoading.value = false;
}
};
// 监听属性变化
watch(
() => props.svgUrl,
() => {
loadSvgContent();
}
);
watch(
() => props.color,
() => {
// 颜色变化时重新处理SVG,不需要重新加载
}
);
// 组件挂载时加载SVG
onMounted(() => {
loadSvgContent();
});
</script>
<style scoped>
.color-svg {
display: inline-block;
/* svg垂直居中 */
vertical-align: middle;
}
.svg-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.color-svg-loading {
display: flex;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
}
.loading-spinner {
width: 20px;
height: 20px;
border: 2px solid #f3f3f3;
border-top: 2px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>
\ No newline at end of file
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
<div class="header-title"> <div class="header-title">
<slot name="title">{{ "社交媒体" }}</slot> <slot name="title">{{ "社交媒体" }}</slot>
</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
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
</div> </div>
</div> </div>
<div class="news-main"> <div class="news-main">
<div class="news-item" v-for="(news, index) in newsList" :key="index" @click="handleToNewsAnalysis(news)"> <div class="news-item" v-for="(news, index) in showNewsList" :key="index" @click="handleToNewsAnalysis(news)">
<div class="left"> <div class="left">
<img :src="news[props.img] ? news[props.img] : DefaultIconNews" alt="" /> <img :src="news[props.img] ? news[props.img] : DefaultIconNews" alt="" />
</div> </div>
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
</template> </template>
<script setup> <script setup>
import { computed } from "vue";
import DefaultIconNews from "@/assets/icons/default-icon-news.png"; import DefaultIconNews from "@/assets/icons/default-icon-news.png";
const props = defineProps({ const props = defineProps({
// 新闻列表数据 // 新闻列表数据
...@@ -54,6 +55,12 @@ const props = defineProps({ ...@@ -54,6 +55,12 @@ const props = defineProps({
default: "content" default: "content"
} }
}); });
const showNewsList = computed(() => {
return props.newsList.slice(0,5)
})
const emit = defineEmits(["item-click", "more-click"]); const emit = defineEmits(["item-click", "more-click"]);
const handleToMoreNews = () => { const handleToMoreNews = () => {
emit("more-click"); emit("more-click");
......
<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';
...@@ -23,12 +27,17 @@ ...@@ -23,12 +27,17 @@
border-radius: 4px; border-radius: 4px;
border: 1px solid var(--color-primary-10); border: 1px solid var(--color-primary-10);
color: var(--color-primary-100); color: var(--color-primary-100);
margin: 10px;
} }
.img { .img {
width: 19px; width: 19px;
height: 20px; height: 20px;
background-image: url("@/assets/icons/model.png");
img {
width: 100%;
height: 100%;
}
} }
.txt { .txt {
...@@ -41,21 +50,12 @@ ...@@ -41,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 @click="onClick(person)"> <el-space @click="onClick(person)">
<ElAvatar :size="48" :src="person[img]" alignment="center" /> <el-avatar :size="48" :src="person[img]" alignment="center" />
<el-space :size="0" direction="vertical" alignment="flex-start"> <el-space :size="0" direction="vertical" alignment="flex-start">
<div class="text-header">{{ person[name] }}</div> <div class="text-header">{{ person[name] }}</div>
<div class="person-position">{{ person[position] }}</div> <div class="person-position">{{ person[position] }}</div>
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
<script setup> <script setup>
import '@/styles/common.scss' import '@/styles/common.scss'
import { ElSpace } from 'element-plus'; import { ElSpace, ElAvatar } from 'element-plus';
const props = defineProps({ const props = defineProps({
// 新闻列表数据 // 新闻列表数据
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
</div> </div>
<div class="box2-main"> <div class="box2-main">
<div class="box2-main-item" v-for="(item, index) in list" :key="index" @click="handleItemClick(item, index)"> <div class="box2-main-item" v-for="(item, index) in showRiskSignalList" :key="index" @click="handleItemClick(item, index)">
<div :class="{ <div :class="{
itemLeftStatus1: item[props.riskLevel] === '特别重大', itemLeftStatus1: item[props.riskLevel] === '特别重大',
itemLeftStatus2: item[props.riskLevel] === '重大风险', itemLeftStatus2: item[props.riskLevel] === '重大风险',
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
<script setup> <script setup>
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { computed } from "vue";
// 接收父组件传递的参数 // 接收父组件传递的参数
const props = defineProps({ const props = defineProps({
// 标题(默认“风险信号”) // 标题(默认“风险信号”)
...@@ -75,6 +76,10 @@ const props = defineProps({ ...@@ -75,6 +76,10 @@ const props = defineProps({
}); });
const showRiskSignalList = computed(() => {
return props.list.slice(0,6)
})
// 定义自定义事件,把点击事件传递给父组件 // 定义自定义事件,把点击事件传递给父组件
const emit = defineEmits(['item-click', 'more-click']); const emit = defineEmits(['item-click', 'more-click']);
...@@ -85,7 +90,6 @@ const handleItemClick = (item, index) => { ...@@ -85,7 +90,6 @@ const handleItemClick = (item, index) => {
// 点击“查看更多” // 点击“查看更多”
const handleMoreClick = () => { const handleMoreClick = () => {
emit('more-click') emit('more-click')
}; };
...@@ -202,11 +206,10 @@ const handleMoreClick = () => { ...@@ -202,11 +206,10 @@ const handleMoreClick = () => {
box-sizing: border-box; box-sizing: border-box;
padding-left: 23px; padding-left: 23px;
padding-right: 30px; padding-right: 30px;
overflow-y: auto;
width: 520px; width: 520px;
height: calc(100% - 160px); height: 313px;
border-radius: 4px; border-radius: 4px;
padding: 15px 30px 10px 23px;
.box2-main-item { .box2-main-item {
width: 463px; width: 463px;
height: 48px; height: 48px;
......
<template>
<el-space :size="16" class="text-tip-1-bold box-color-prefix">
<div class="color-prefix"></div>
<slot></slot>
</el-space>
</template>
<script setup lang="ts">
import '@/styles/common.scss';
import { ElSpace } from 'element-plus';
const props = defineProps({
color: {
type: String,
default: 'var(--color-primary-100)'
},
height: {
type: String,
default: '16px'
}
})
</script>
<style lang="scss" scoped>
.color-prefix {
width: 8px;
height: v-bind(height);
background-color: v-bind(color);
}
.box-color-prefix {
color: v-bind(color);
}
</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",
......
...@@ -9,7 +9,7 @@ const companyPagesRoutes = [ ...@@ -9,7 +9,7 @@ const companyPagesRoutes = [
{ {
path: "/companyPages/:id", path: "/companyPages/:id",
name: "companyPages", name: "companyPages",
component: companyPages, component: companyPages2,
meta: { meta: {
title: "企业主页", title: "企业主页",
dynamicTitle: true dynamicTitle: true
...@@ -19,7 +19,7 @@ const companyPagesRoutes = [ ...@@ -19,7 +19,7 @@ const companyPagesRoutes = [
{ {
path: "/companyPages2/:id", path: "/companyPages2/:id",
name: "companyPages2", name: "companyPages2",
component: companyPages2, component: companyPages,
meta: { meta: {
title: "企业主页", title: "企业主页",
dynamicTitle: true dynamicTitle: true
......
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;
...@@ -4,17 +4,46 @@ ...@@ -4,17 +4,46 @@
border-radius: 10px; border-radius: 10px;
border: 1px solid var(--bg-white-100); border: 1px solid var(--bg-white-100);
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1); box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.15);
} }
.flex-display { .flex-display {
display: flex; display: flex;
} }
.flex-display-center {
@extend .flex-display;
justify-content: center;
align-items: center;
}
.flex-display-end {
@extend .flex-display;
justify-content: flex-end;
align-items: center;
}
.flex-display-start {
@extend .flex-display;
justify-content: flex-start;
align-items: center;
}
.flex-fill { .flex-fill {
flex: 1; flex: 1;
} }
.full-width {
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;
...@@ -39,13 +68,20 @@ ...@@ -39,13 +68,20 @@
/***文本样式***/ /***文本样式***/
.text-base { .text-base {
color: var(--color-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;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 32px; font-size: 32px;
} }
...@@ -63,7 +99,7 @@ ...@@ -63,7 +99,7 @@
//1级标题 //1级标题
.text-title-1 { .text-title-1 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 24px; font-size: 24px;
} }
...@@ -81,7 +117,7 @@ ...@@ -81,7 +117,7 @@
//2级标题 //2级标题
.text-title-2 { .text-title-2 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 20px; font-size: 20px;
line-height: 26px; line-height: 26px;
} }
...@@ -101,7 +137,7 @@ ...@@ -101,7 +137,7 @@
//3级标题 //3级标题
.text-title-3 { .text-title-3 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 18px; font-size: 18px;
line-height: 24px; line-height: 24px;
} }
...@@ -148,7 +184,7 @@ ...@@ -148,7 +184,7 @@
//1级提示文字 //1级提示文字
.text-tip-1 { .text-tip-1 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 16px; font-size: 16px;
line-height: 24px; line-height: 24px;
} }
...@@ -161,7 +197,7 @@ ...@@ -161,7 +197,7 @@
//2级提示文字 //2级提示文字
.text-tip-2 { .text-tip-2 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
} }
...@@ -174,60 +210,100 @@ ...@@ -174,60 +210,100 @@
//3级提示文字 //3级提示文字
.text-tip-3 { .text-tip-3 {
@extend .text-base; @extend .text-base;
color: var(--color-primary-90); color: var(--text-primary-90);
font-size: 12px; font-size: 12px;
} }
.main-color{ .main-color {
color: rgb(5, 95, 194); color: rgb(5, 95, 194);
} }
// 业务主色 高亮背景 // 业务主色 高亮背景
.color-bg-active{ .color-bg-active {
background: rgb(246, 250, 255); background: rgb(246, 250, 255);
} }
// 黑色 // 黑色
.text-primary-clor{ .text-primary-clor {
color: #0a121e; color: #0a121e;
} }
// 黑色90% / 主标题文字颜色 // 黑色90% / 主标题文字颜色
.text-primary-90-clor{ .text-primary-90-clor {
color: #222934; color: #222934;
} }
// 黑色80% / 小标题文字颜色 // 黑色80% / 小标题文字颜色
.text-primary-80-clor{ .text-primary-80-clor {
color: #3b414b; color: #3b414b;
} }
// 黑色65% / 正文颜色 // 黑色65% / 正文颜色
.text-primary-65-clor{ .text-primary-65-clor {
color: #5f656c; color: #5f656c;
} }
// 黑色50% // 黑色50%
.text-primary-50-clor{ .text-primary-50-clor {
color: #84888e; color: #84888e;
} }
// 黑色10% // 黑色10%
.bg-black-10{ .bg-black-10 {
background: #E6E7E8; background: #E6E7E8;
} }
// 黑色5% // 黑色5%
.bg-black-5{ .bg-black-5 {
background: #EAECEE; background: #EAECEE;
} }
// 黑色2% // 黑色2%
.bg-black-2{ .bg-black-2 {
background: #F7F8F9; background: #F7F8F9;
} }
// 白色主色 // 白色主色
.bg-white-100{ .bg-white-100 {
background: #FFFFFF; background: #FFFFFF;
} }
// 红色
.color-red-100 {
color: rgb(206, 79, 81);
}
// 红色10%背景
.bg-red-10 {
background: rgba(206, 79, 81, 0.1);
}
// 橙色
.color-orange-100 {
color: rgb(255, 149, 77)
}
// 橙色10%背景
.bg-orange-10 {
background: rgba(255, 149, 77, 0.1)
}
// 黄色
.color-yellow-100 {
color: rgb(232, 189, 11);
}
// 黄色10%背景
.bg-yellow-10 {
background: rgba(232, 189, 11, 0.1);
}
// 绿色
.color-green-100 {
color: rgb(33, 129, 57);
}
// 绿色10%背景
.bg-green-10 {
background: rgba(33, 129, 57, 0.1);
}
\ No newline at end of file
...@@ -5,7 +5,7 @@ const span = 12 ...@@ -5,7 +5,7 @@ const span = 12
</script> </script>
<template> <template>
<el-row class="wrapper"> <el-row class="wrapper layout-grid-line">
<el-col :span="span"> <el-col :span="span">
<pre> <pre>
{{ `import '@/styles/common.scss'; {{ `import '@/styles/common.scss';
...@@ -14,18 +14,36 @@ const span = 12 ...@@ -14,18 +14,36 @@ const span = 12
</template> </template>
`}} `}}
</pre> </pre>
<div class="common-padding">
<div class="background-as-card"> <div class="background-as-card">
<div v-for="item in [1, 2, 3, 4]" :key="item"> <div v-for="item in [1, 2, 3, 4]" :key="item">
{{ item }} {{ item }}
</div> </div>
</div> </div>
</div>
</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-col>
</el-row> </el-row>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.wrapper { .wrapper {
background-color: rgba(0, 0, 0, 0.068); // background-color: rgba(0, 0, 0, 0.068);
padding: 10px; padding: 10px;
} }
</style> </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="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
<script setup lang="ts">
import { ElRow, ElCol } from 'element-plus';
import '@/styles/common.scss'
import ColorSvg from '@/components/base/images/ColorSvg.vue';
const span = 12
</script>
<template>
<el-row class="wrapper layout-grid-line">
<el-col :span="span">
<pre>
{{ `import ColorSvg from '@/components/base/images/ColorSvg.vue';
<template>
<color-svg :size="64" :svg-url="'/icon/arrow-up.svg'" :color="'var(--color-orange-100)'" />
<color-svg :size="16" :svg-url="'/icon/arrow-up.svg'" :color="'var(--color-green-100)'" />
</template>
`}}
</pre>
<color-svg :size="64" :svg-url="'/icon/arrow-up.svg'" :color="'var(--color-orange-100)'" />
<color-svg :size="16" :svg-url="'/icon/arrow-up.svg'" :color="'var(--color-green-100)'" />
</el-col>
</el-row>
</template>
<style lang="scss" scoped></style>
\ No newline at end of file
<script setup lang="ts">
import { ElRow, ElCol } from 'element-plus';
import '@/styles/common.scss'
import PersonAvatar from '@/components/base/people/PersonAvatar.vue';
const span = 12
</script>
<template>
<el-row class="wrapper layout-grid-line">
<el-col :span="span">
<pre>
{{ `import PersonAvatar from '@/components/base/people/PersonAvatar.vue';
<template>
<person-avatar class="person-avatar" :person="{
name: '张三',
position: 'CEO,CTO',
avatarUrl: '/icon/美国.png'
}">
</person-avatar>
</template>
`}}
</pre>
<person-avatar class="person-avatar" :person="{
name: '张三',
position: 'CEO,CTO',
avatarUrl: '/icon/美国.png'
}">
</person-avatar>
</el-col>
</el-row>
</template>
<style lang="scss" scoped>
.person-avatar {
width: 200px;
}
</style>
\ No newline at end of file
...@@ -8,7 +8,7 @@ const span = 12 ...@@ -8,7 +8,7 @@ const span = 12
</script> </script>
<template> <template>
<el-row> <el-row class="layout-grid-line">
<el-col :span="span"> <el-col :span="span">
<pre> <pre>
{{ `import '@/styles/radio.scss'; {{ `import '@/styles/radio.scss';
...@@ -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>
......
<script setup lang="ts"> <script setup lang="ts">
import { ElTabs, ElTabPane, ElSpace, ElRow, ElCol } from 'element-plus'; import { ElTabs, ElTabPane, ElSpace, ElRow, ElCol } from 'element-plus';
import { ref } from 'vue';
import '@/styles/tabs.scss' import '@/styles/tabs.scss'
const span = 12 const span = 12
</script> </script>
<template> <template>
<el-row> <el-row :gutter="16" class="layout-grid-line">
<el-col :span="span"> <el-col :span="span">
<pre> <pre>
{{ `import '@/styles/tabs.scss'; {{ `import '@/styles/tabs.scss';
...@@ -22,6 +21,21 @@ const span = 12 ...@@ -22,6 +21,21 @@ const span = 12
<el-tab-pane label="tab3">tab3</el-tab-pane> <el-tab-pane label="tab3">tab3</el-tab-pane>
</el-tabs> </el-tabs>
</el-col> </el-col>
<el-col :span="span">
<pre>{{ `import '@/styles/tabs.scss';\n<template>
//确保父级position: relative;
<el-tabs tabPosition="left" class="tabs-nav-no-wrap left-float-nav-tabs">
</template>
`}}
</pre>
<div style="margin-left: 160px;position: relative; height: 200px;">
<el-tabs tabPosition="left" class="tabs-nav-no-wrap left-float-nav-tabs">
<el-tab-pane label="tab1">tab1</el-tab-pane>
<el-tab-pane label="tab2">tab2</el-tab-pane>
<el-tab-pane label="tab3">tab3</el-tab-pane>
</el-tabs>
</div>
</el-col>
</el-row> </el-row>
</template> </template>
......
<script setup lang="ts">
import { ElSpace, ElRow, ElCol } from 'element-plus';
import '@/styles/tabs.scss'
import ColorPrefixTitle from '@/components/base/texts/ColorPrefixTitle.vue';
import AiTipPane from '@/components/base/panes/AiTipPane.vue';
import CommonText from '@/components/base/texts/CommonText.vue';
const span = 12
</script>
<template>
<el-row class="layout-grid-line">
<el-col :span="span">
<pre>
{{ `import CommonText from '@/components/base/texts/CommonText.vue';
<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="var(--color-yellow-100)">科技领域</color-prefix-title>
<color-prefix-title color="red">科技领域</color-prefix-title>
</template>
`}}
</pre>
<el-space direction="vertical">
<color-prefix-title>科技领域</color-prefix-title>
<color-prefix-title color="var(--color-yellow-100)">科技领域</color-prefix-title>
<color-prefix-title color="red">科技领域</color-prefix-title>
</el-space>
</el-col>
<el-col :span="span">
<pre>{{ `import AiTipPane from '@/components/base/panes/AiTipPane.vue';\n<template>
<ai-tip-pane>huidadadadadasda</ai-tip-pane>
</template>
`}}
</pre>
<ai-tip-pane>huidadadadadasda</ai-tip-pane>
</el-col>
</el-row>
</template>
<style lang="scss" scoped></style>
\ No newline at end of file
<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
.layout-grid-line {
.el-col {
border: 1px double var(--bg-black-5);
}
}
\ No newline at end of file
<template> <template>
<el-scrollbar> <el-scrollbar>
<div class="common-page"> <div class="common-page box">
<el-space direction="vertical" :size="20" alignment="flex-start" fill> <el-space direction="vertical" :size="20" alignment="flex-start" fill>
<div class="text-title-0-show">开发样式</div> <div class="text-title-0-show">开发样式</div>
<div class="text-title-1-show">样式变量</div> <div class="text-title-1-show">样式变量</div>
...@@ -8,17 +8,37 @@ ...@@ -8,17 +8,37 @@
<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>
<el-tabs tabPosition="left"> <div style="position: relative; min-height: 700px;">
<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 />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="文本" lazy>
<text-page />
</el-tab-pane>
<el-tab-pane label="图片" lazy>
<images-page />
</el-tab-pane>
<el-tab-pane label="单选框" lazy> <el-tab-pane label="单选框" lazy>
<radio-page /> <radio-page />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="选项卡" lazy> <el-tab-pane label="选项卡" lazy>
<tabs-page /> <tabs-page />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="人物" lazy>
<people-page />
</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>
</el-space> </el-space>
</div> </div>
</el-scrollbar> </el-scrollbar>
...@@ -26,12 +46,23 @@ ...@@ -26,12 +46,23 @@
<script setup> <script setup>
import "@/styles/container.scss" import "@/styles/container.scss"
import "./devStyle.scss"
import TextStyle from './textStyle.vue'; import TextStyle from './textStyle.vue';
import ConstStyle from './constStyle.vue'; import ConstStyle from './constStyle.vue';
import { ElTabs, ElTabPane, ElSpace } from "element-plus"; import { ElTabs, ElTabPane, ElSpace } from "element-plus";
import RadioPage from './RadioPage/index.vue'; import RadioPage from './RadioPage/index.vue';
import TabsPage from './TabsPage/index.vue'; import TabsPage from './TabsPage/index.vue';
import CommonPage from './CommonPage/index.vue'; import CommonPage from './CommonPage/index.vue';
import TextPage from './TextPage/index.vue';
import ImagesPage from './Images/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> <style lang="scss" scoped>
\ No newline at end of file .box {
padding-bottom: 20px;
}
</style>
\ No newline at end of file
...@@ -96,7 +96,19 @@ const tableData = ref([ ...@@ -96,7 +96,19 @@ const tableData = ref([
{ name: '白色主色', className: 'bg-white-100'}, { name: '白色主色', className: 'bg-white-100'},
{name: '主色', className: 'main-color'}, {name: '主色', className: 'main-color'},
{name: '高亮背景色', className: 'color-bg-active'} {name: '高亮背景色', className: 'color-bg-active'},
{name: '红色', className: 'color-red-100'},
{name: '红色10%-背景', className: 'bg-red-10'},
{name: '橙色', className: 'color-orange-100'},
{name: '橙色10%-背景', className: 'bg-orange-10'},
{name: '黄色', className: 'color-yellow-100'},
{name: '黄色10%-背景', className: 'bg-yellow-10'},
{name: '绿色', className: 'color-green-100'},
{name: '绿色10%-背景', className: 'bg-green-10'},
]) ])
</script> </script>
......
...@@ -2,7 +2,18 @@ ...@@ -2,7 +2,18 @@
/***通用页面***/ /***通用页面***/
.common-page { .common-page {
margin: 16px auto; 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
...@@ -17,3 +17,34 @@ ...@@ -17,3 +17,34 @@
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
...@@ -25,20 +25,13 @@ ...@@ -25,20 +25,13 @@
/*定义字体*/ /*定义字体*/
.el-tabs__item:not(.disinheritance .el-tabs__item) { .el-tabs__item:not(.disinheritance .el-tabs__item) {
font-size: 20px; @extend .text-title-2;
font-family: "Source Han Sans CN-Regular"; color: var(--text-primary-80-color);
font-weight: Regular;
line-height: 26px;
height: 50px;
} }
/*激活时按钮样式*/ /*激活时按钮样式*/
.el-tabs__item.is-active:not(.disinheritance .el-tabs__item) { .el-tabs__item.is-active:not(.disinheritance .el-tabs__item) {
@extend .text-title-1-bold;
font-size: 24px;
font-family: "Source Han Sans CN-Bold";
font-weight: Bold;
color: var(--color-primary-100); color: var(--color-primary-100);
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
...@@ -48,27 +41,54 @@ ...@@ -48,27 +41,54 @@
} }
} }
/***允许tab-pane内部元素溢出***/
.tab-pane-overflow-visible {
.el-tab-pane {
overflow: visible !important;
}
// 同时确保父容器也允许溢出
.el-tabs__content {
overflow: visible !important;
}
}
/***tabs-bar左边悬浮***/ /***tabs-bar左边悬浮***/
.left-float-nav-tabs { .left-float-nav-tabs {
.el-tabs__header.is-left {
position: absolute;
left: -140px;
top: 0px;
// .el-tabs__header { .el-tabs__nav {
gap: 16px;
position: relative; }
overflow: visible;
transform: translateZ(0);
/* 双重保障 */
/* 创建新的渲染层 */ .el-tabs__active-bar {
/* 创建新的层叠上下文 */ background-color: transparent;
.el-tabs__header { right: 12px;
top: 11px;
height: 8px !important;
border-top: 6px solid transparent;
/* 顶部边框透明 */
border-bottom: 6px solid transparent;
/* 底部边框透明 */
border-left: 8px solid var(--bg-white-100);
/* 左侧边框有颜色 */
}
}
// position: absolute; .el-tabs__item.is-left {
left: 0; @extend .text-tip-1;
top: 0; color: var(--text-primary-65-color);
z-index: 999; height: 32px;
background-color: red; padding: 4px 26px 4px 28px;
// margin-left: -20px; border-radius: 16px 16px 16px 16px;
justify-content: center;
} }
// } .el-tabs__item.is-left.is-active {
color: var(--bg-white-100);
background-color: var(--color-primary-100);
}
} }
\ 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) {
......
...@@ -37,22 +37,55 @@ ...@@ -37,22 +37,55 @@
</div> </div>
</div> </div>
</div> --> </div> -->
<el-dialog v-model="tooltipVisible" width="690px"> <el-dialog v-model="tooltipVisible" width="1298px">
<div class="tooltip-header flex-display"> <div class="tooltip-header flex-display">
<div class="tooltip-header-left text-title-3-bold">{{ currentDate }}</div> <div class="tooltip-header-left text-title-3-bold">{{ currentDate }}</div>
<div class="tooltip-header-right text-title-3-show">{{ `${currentOrgNum}个部门/${currentDetailList.length}项举措` }} <div class="tooltip-header-right text-title-3-show">{{ `涉及${currentDetailList.length}项行政令` }}
</div> </div>
</div> </div>
<div class="tooltip-main"> <div class="tooltip-main">
<div class="tooltip-main-item" v-for="item, index in currentDetailList" :key="index"> <div class="tooltip-main-item" v-for="item, index in currentDetailList" :key="index">
<div class="item-footer color-bg-active flex-display" @click="handleToDetail(item)"> <div class="item-main-header flex-display" @click="handleToDetail(item)">
<div class="footer-text text-tip-2 main-color">{{ item.name }}</div> <div class="item-main-header-left">
<div class="footer-arrow"> <div class="item-main-header-type color-bg-active text-tip-1 main-color">{{ '政令' }}</div>
<img src="./right-arrow.svg" alt=""> <div class="text-title-3-bold main-color">{{ item.administrativeOrderInfoTitle }}</div>
</div> </div>
<div class="item-main-header-right text-tip-1-bold main-color">
{{ `涉及${item.orgNum}个部门` }}
</div> </div>
<div class="item-list" v-for="val, idx in item.list" :key="idx"> </div>
<div class="item-header flex-display"> <div class="item-list">
<div class="item-list-item flex-display" v-for="val, idx in item.innerList" :key="idx">
<div class="item-list-left flex-display">
<div class="id text-bold main-color color-bg-active">{{ idx + 1 }}</div>
<div class="info">
<div class="content text-regular text-primary-80-clor">{{ val.name }}</div>
<div class="area-box flex-display">
<AreaTag v-for="tag, tagIdx in val.techDomainList" :key="tagIdx" :tagName="tag"></AreaTag>
</div>
</div>
</div>
<div class="item-list-right ">
<div class="right-list-item flex-display" v-for="org, orgIdx in val.orgList" :key="orgIdx">
<div class="org-box flex-display">
<div class="logo">
<img style="width: 100%; height: 100%;" :src="org.orgLogoUrl" alt="">
</div>
<div class="org-name text-tip-1 text-primary-80-clor">{{ org.orgName }}</div>
</div>
<div class="status">
<div class="status-on text-tip-2" v-if="org.stauts === 2">{{ '已落实' }}</div>
<div class="status-off text-tip-2" v-else>{{ '未落实' }}</div>
</div>
</div>
</div>
</div>
<!-- <div class="item-header flex-display">
<div class="item-header-left flex-display"> <div class="item-header-left flex-display">
<div class="logo"> <div class="logo">
<img style="width:100%; height: 100%" :src="val.orgLogoUrl" alt="logo"> <img style="width:100%; height: 100%" :src="val.orgLogoUrl" alt="logo">
...@@ -67,7 +100,7 @@ ...@@ -67,7 +100,7 @@
<AreaTag v-for="tag, idxx in val.techDomainList.slice(0, 3)" :key="idxx" :tagName="tag"></AreaTag> <AreaTag v-for="tag, idxx in val.techDomainList.slice(0, 3)" :key="idxx" :tagName="tag"></AreaTag>
</div> </div>
</div> </div>
<div class="item-content text-compact">{{ val.name }}</div> <div class="item-content text-compact">{{ val.name }}</div> -->
</div> </div>
...@@ -184,7 +217,13 @@ function buildDataMap(arr) { ...@@ -184,7 +217,13 @@ function buildDataMap(arr) {
}); });
} }
const d = map.get(date); const d = map.get(date);
// d.count += 1;
// 按 administrativeOrderInfoTitle 去重计数
if (!d._titleSet) d._titleSet = new Set();
if (item.administrativeOrderInfoTitle && !d._titleSet.has(item.administrativeOrderInfoTitle)) {
d._titleSet.add(item.administrativeOrderInfoTitle);
d.count += 1; d.count += 1;
}
if (item.status === 2) d.resolved += 1; if (item.status === 2) d.resolved += 1;
if (d.logos.length < 3 && item.orgLogoUrl) d.logos.push(item.orgLogoUrl); if (d.logos.length < 3 && item.orgLogoUrl) d.logos.push(item.orgLogoUrl);
d.status = 'has_events'; d.status = 'has_events';
...@@ -373,7 +412,7 @@ function getOption() { ...@@ -373,7 +412,7 @@ function getOption() {
groupChildren.push({ groupChildren.push({
type: 'text', type: 'text',
style: { style: {
text: `${item.count}项`, text: `${item.count}项行政令`,
x: x + 8, x: x + 8,
y: y + cellHeight - 12, y: y + cellHeight - 12,
textAlign: 'left', textAlign: 'left',
...@@ -430,6 +469,8 @@ function getOption() { ...@@ -430,6 +469,8 @@ function getOption() {
// 点击政令,跳转详情 // 点击政令,跳转详情
const handleToDetail = (item) => { const handleToDetail = (item) => {
console.log('item', item);
const path = "/decreeLayout/overview/introduction"; const path = "/decreeLayout/overview/introduction";
const query = { id: item.orderId }; const query = { id: item.orderId };
const route = router.resolve({ path, query }); const route = router.resolve({ path, query });
...@@ -452,22 +493,47 @@ onMounted(() => { ...@@ -452,22 +493,47 @@ onMounted(() => {
if (list.length > 0) { if (list.length > 0) {
currentDate.value = date currentDate.value = date
// 按 administrativeOrderInfoTitle 分类 // 按 administrativeOrderInfoTitle 分类
// const grouped = [];
// const map = new Map();
// 按 sanctionId 分类,sanctionId 相同合为一个元素,形式为 { name: 'xxx', info: {} }
const grouped = []; const grouped = [];
const map = new Map(); const titleMap = new Map();
list.forEach(item => { list.forEach(item => {
const key = item.administrativeOrderInfoTitle; const title = item.administrativeOrderInfoTitle || '';
if (!map.has(key)) { if (!titleMap.has(title)) {
map.set(key, { name: key, list: [] }); titleMap.set(title, { administrativeOrderInfoTitle: title, innerList: [], orgNum: 0, orderId: item.orderId });
grouped.push(map.get(key)); grouped.push(titleMap.get(title));
}
// 统计 orgNum
const group = titleMap.get(title);
// 用 Set 统计不同 orgName
if (!group._orgSet) group._orgSet = new Set();
group._orgSet.add(item.orgName);
group.orgNum = group._orgSet.size;
// 按 sanctionId 分类
const innerList = titleMap.get(title).innerList;
let sanctionGroup = innerList.find(g => g.sanctionId === item.sanctionId);
if (!sanctionGroup) {
sanctionGroup = {
sanctionId: item.sanctionId,
name: item.name,
techDomainList: item.techDomainList || [],
orgList: [],
info: []
};
innerList.push(sanctionGroup);
} }
map.get(key).list.push(item); sanctionGroup.orgList.push({
orgName: item.orgName,
orgLogoUrl: item.orgLogoUrl,
status: item.status
});
sanctionGroup.info.push({ ...item });
}); });
currentDetailList.value = grouped; currentDetailList.value = grouped;
console.log('currentDetailList', currentDetailList.value); console.log('currentDetailList', currentDetailList.value);
// 统计不同 orgName 的个数
const orgNames = new Set(list.map(item => item.orgName));
currentOrgNum.value = orgNames.size
tooltipVisible.value = true tooltipVisible.value = true
} else { } else {
tooltipVisible.value = false tooltipVisible.value = false
...@@ -503,27 +569,39 @@ onBeforeUnmount(() => { ...@@ -503,27 +569,39 @@ onBeforeUnmount(() => {
/* pointer-events: none; */ /* pointer-events: none; */
/* 让鼠标事件穿透到图表,避免闪烁 */ /* 让鼠标事件穿透到图表,避免闪烁 */
z-index: 1000; z-index: 1000;
width: 670px; width: 1278px;
background: rgb(255, 255, 255); background: rgb(255, 255, 255);
} }
.tooltip-header { .tooltip-header {
height: 50px; height: 56px;
box-sizing: border-box; box-sizing: border-box;
padding: 13px 24px; padding: 12px 24px;
justify-content: space-between; justify-content: space-between;
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
} }
.tooltip-main { .tooltip-header-left{
padding: 0 24px 10px; margin-top: 10px;
}
.tooltip-header-right{
height: 34px;
background: rgba(206, 79, 81, 0.1);
border-radius: 17px;
color: rgba(206, 79, 81);
padding: 5px 12px;
} }
/* .tooltip-main {
padding: 0 24px 10px;
} */
.tooltip-main-item { .tooltip-main-item {
padding: 10px 0; padding: 10px 0;
width: 622px; width: 1254px;
/* height: 144px; */ /* height: 144px; */
border-bottom: 1px solid rgb(234, 236, 238); border-bottom: 1px solid rgb(234, 236, 238);
} }
...@@ -540,6 +618,50 @@ onBeforeUnmount(() => { ...@@ -540,6 +618,50 @@ onBeforeUnmount(() => {
} }
.item-list-item{
padding: 12px 12px 16px 20px;
border-bottom: 1px solid rgb(234, 236, 238);
}
.item-list-left{
width: 848px;
}
.area-box {
margin-top: 8px;
gap: 8px;
}
.id{
width: 30px;
height: 30px;
border-radius: 15px;
text-align: center;
line-height: 30px;
}
.info{
width: 800px;
}
.item-list-right{
width: 356px;
justify-content: space-between;
}
.right-list-item {
justify-content: space-between;
margin-bottom: 8px;
}
.org-box {
gap: 8px;
}
.org-name{
width: 234px;
}
.logo { .logo {
width: 24px; width: 24px;
height: 24px; height: 24px;
...@@ -559,36 +681,33 @@ onBeforeUnmount(() => { ...@@ -559,36 +681,33 @@ onBeforeUnmount(() => {
background: rgba(206, 79, 81, 0.1); background: rgba(206, 79, 81, 0.1);
} }
.item-content {
height: 48px;
display: -webkit-box;
-webkit-line-clamp: 2;
/* 限制显示2行 */
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-word;
/* 可选:防止单词被截断 */
}
.item-header-right { .item-header-right {
gap: 8px; gap: 8px;
} }
.item-footer { .item-main-header {
width: 622px; width: 1254px;
height: 40px; height: 68px;
padding: 0 8px; padding: 0 8px;
margin-top: 4px;
border-radius: 4px; border-radius: 4px;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
border-bottom: 1px solid rgb(234, 236, 238);
}
.item-main-header-left {
display: flex;
gap: 8px;
align-items: center;
} }
.footer-arrow { .item-main-header-type {
width: 20px; height: 28px;
height: 20px; border-radius: 4px;
padding: 0 8px;
line-height: 28px;
text-align: center;
} }
</style> </style>
...@@ -69,8 +69,11 @@ ...@@ -69,8 +69,11 @@
<div <div
class="item-left" class="item-left"
:class="{ :class="{
itemLeftStatus1: item.signalLevel === '特别重大', 'item-status-1': item.signalLevel === '特别重大',
itemLeftStatus2: item.signalLevel === '重大风险' 'item-status-2': item.signalLevel === '重大风险',
'item-status-3': item.signalLevel === '较大风险',
'item-status-4': item.signalLevel === '一般风险',
'item-status-5': item.signalLevel === '低风险'
}" }"
> >
{{ item.signalLevel ? item.signalLevel : "一般风险" }} {{ item.signalLevel ? item.signalLevel : "一般风险" }}
...@@ -1224,14 +1227,29 @@ onUnmounted(() => { ...@@ -1224,14 +1227,29 @@ onUnmounted(() => {
// } // }
// } // }
.itemLeftStatus1 { .item-status-1 {
color: rgba(245, 34, 45, 1) !important; color: var(--color-red-100) !important;
background: rgba(255, 241, 240) !important; background: var(--color-red-10) !important;
} }
.itemLeftStatus2 { .item-status-2 {
color: rgba(250, 140, 22, 1) !important; color: var(--color-orange-100) !important;
background: rgba(255, 247, 230, 1) !important; background: var(--color-orange-10) !important;
}
.item-status-3 {
color: var(--color-yellow-100) !important;
background: var(--color-yellow-10) !important;
}
.item-status-4 {
color: var(--color-green-100) !important;
background: var(--color-green-10) !important;
}
.item-status-5 {
color: var(--color-primary-100) !important;
background: var(--color-primary-10) !important;
} }
.item-left { .item-left {
...@@ -1302,12 +1320,12 @@ onUnmounted(() => { ...@@ -1302,12 +1320,12 @@ onUnmounted(() => {
// background: var(--color-bg-hover); // background: var(--color-bg-hover);
// } // }
.itemLeftStatus1 { .item-status-1 {
color: rgba(245, 34, 45, 1) !important; color: rgba(245, 34, 45, 1) !important;
background: rgba(255, 241, 240) !important; background: rgba(255, 241, 240) !important;
} }
.itemLeftStatus2 { .item-status-2 {
color: rgba(250, 140, 22, 1) !important; color: rgba(250, 140, 22, 1) !important;
background: rgba(255, 247, 230, 1) !important; background: rgba(255, 247, 230, 1) !important;
} }
......
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论