提交 de9b61c0 authored 作者: 刘宇琪's avatar 刘宇琪

刘宇琪 更新智库人物页面

上级 592d75b3
<template>
<div class="resume-card">
<div class="resume-card__header">
<div class="resume-card__indicator"></div>
<div class="resume-card__title">{{ title }}</div>
<div class="resume-card__tabs" v-if="tabs.length > 0">
<div
v-for="(tab, i) in tabs"
:key="tab.key"
class="resume-card__tab"
:class="{ 'resume-card__tab--active': activeTab === tab.key }"
@click="switchTab(tab.key)"
>{{ tab.label }}</div>
</div>
<div class="resume-card__actions">
<img
:src="downloadIcon"
alt="下载"
class="resume-card__action-btn"
@click="$emit('download')"
/>
<img
:src="collectIcon"
alt="收藏"
class="resume-card__action-btn"
@click="$emit('collect')"
/>
</div>
</div>
<div class="resume-card__body">
<div v-for="(item, index) in currentList" :key="index" class="resume-item">
<img :src="timelineIcon" alt="" class="resume-item__icon" />
<div class="resume-item__time">{{ item.startTime + '-' + item.endTime }}</div>
<div class="resume-item__title">{{ item.orgName + '|' + item.jobName }}</div>
<div class="resume-item__content">{{ item.content }}</div>
<div class="resume-item__door" v-if="item.door">
<img :src="doorIcon" alt="" />
<span>{{ item.door }}</span>
</div>
</div>
</div>
</div>
</template>
<script>
import defaultDownloadIcon from "../assets/下载按钮.png";
import defaultCollectIcon from "../assets/收藏按钮.png";
import defaultTimelineIcon from "../assets/icon01.png";
import defaultDoorIcon from "../assets/icon02.png";
export default {
name: "ResumeCard",
props: {
/** 卡片标题 */
title: {
type: String,
default: "生涯履历",
},
/**
* Tab 配置数组,示例:
* [{ key: "career", label: "职业履历" }, { key: "edu", label: "教育履历" }]
* 不传则无 tab,直接显示 list 数据
*/
tabs: {
type: Array,
default: () => [],
},
/**
* 无 tab 时的数据数组;有 tab 时忽略此 prop,使用 dataMap
*/
list: {
type: Array,
default: () => [],
},
/**
* 有 tab 时的数据映射,key 对应 tabs[].key,示例:
* { career: [...], edu: [...] }
*/
dataMap: {
type: Object,
default: () => ({}),
},
/** 自定义图标路径(可选) */
downloadIcon: {
type: String,
default: defaultDownloadIcon,
},
collectIcon: {
type: String,
default: defaultCollectIcon,
},
timelineIcon: {
type: String,
default: defaultTimelineIcon,
},
doorIcon: {
type: String,
default: defaultDoorIcon,
},
},
emits: ["download", "collect", "tab-change"],
data() {
return {
activeTab: this.tabs.length > 0 ? this.tabs[0].key : "",
};
},
computed: {
/** 当前展示的列表数据 */
currentList() {
if (this.tabs.length > 0 && this.activeTab) {
return this.dataMap[this.activeTab] || [];
}
return this.list;
},
},
methods: {
switchTab(key) {
this.activeTab = key;
this.$emit("tab-change", key);
},
},
};
</script>
<style scoped lang="scss">
.resume-card {
width: 520px;
min-height: 200px;
background-color: rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
}
.resume-card__header {
width: 100%;
height: 80px;
display: flex;
align-items: center;
padding: 14px 12px 40px 0;
}
.resume-card__indicator {
width: 8px;
height: 20px;
background-color: rgb(5, 95, 194);
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
margin-right: 14px;
}
.resume-card__title {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.resume-card__tabs {
display: flex;
align-items: center;
gap: 12px;
margin-left: 20px;
}
.resume-card__tab {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
padding: 2px 12px;
border-radius: 4px;
border: 1px solid transparent;
cursor: pointer;
transition: all 0.2s;
&:hover {
color: rgb(5, 95, 194);
}
&--active {
color: rgb(5, 95, 194);
font-weight: 700;
border-color: rgb(5, 95, 194);
background-color: rgba(5, 95, 194, 0.05);
}
}
.resume-card__actions {
margin-left: auto;
display: flex;
align-items: center;
gap: 4px;
}
.resume-card__action-btn {
width: 28px;
height: 28px;
cursor: pointer;
}
.resume-card__body {
width: 480px;
margin-left: 16px;
padding-bottom: 20px;
}
.resume-item {
width: 454px;
// margin-bottom: 60px;
margin-left: 26px;
position: relative;
}
.resume-item__icon {
width: 14px;
height: 12.13px;
position: absolute;
top: 8px;
left: -26px;
}
.resume-item__time {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 30px;
color: rgb(5, 95, 194);
margin-bottom: 8px;
}
.resume-item__title {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 30px;
color: rgb(59, 65, 75);
margin-bottom: 8px;
}
.resume-item__content {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
margin-bottom: 8px;
}
.resume-item__door {
width: 300px;
height: 32px;
display: flex;
align-items: center;
padding: 4px 0 4px 11px;
border-radius: 4px;
background-color: rgba(255, 246, 240, 1);
border: 1px solid rgba(250, 140, 22, 0.4);
img {
width: 20px;
height: 24px;
margin-right: 10px;
}
span {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgba(255, 149, 77, 1);
}
}
</style>
......@@ -118,7 +118,7 @@ const getCharacterGlobalInfoFn = async () => {
const res = await getCharacterGlobalInfo(params);
if (res.code === 200) {
console.log("人物全局信息", res);
console.log("人物全局信息4", res);
if (res.data) {
characterInfo.value = res.data;
}
......
/**
* 新闻动态模块 Mock API
*/
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const NEWS_TITLES = [
'解决春节回家难的问题',
'全国新能源汽车产销双增长',
'人工智能大模型发展新趋势',
'量子计算芯片突破性进展',
'深海资源开发战略规划出台',
'航空航天技术再创新高',
'生物医药产业集群加速形成',
'新材料研发助力制造升级',
'半导体产业链自主可控提速',
'清洁能源装机容量再创纪录',
'5G通信网络覆盖全面铺开',
'极地科考取得重要发现',
]
const NEWS_SOURCES = ['河南新闻', '新华社', '人民日报', '中新网', '科技日报', '央视新闻']
/**
* 获取新闻列表
*/
export async function fetchNewsList(params = {}) {
await delay(300)
const page = params.page || 1
const pageSize = params.pageSize || 12
const total = 1059
const list = Array.from({ length: pageSize }, (_, i) => ({
id: (page - 1) * pageSize + i + 1,
title: NEWS_TITLES[i % NEWS_TITLES.length],
date: '2025年6月26日',
source: NEWS_SOURCES[i % NEWS_SOURCES.length],
image: null,
}))
return { code: 200, data: { list, total } }
}
<template>
<div class="news-card">
<div class="news-card-image">
<img v-if="item.imageUrl" :src="item.imageUrl" :alt="item.name" />
<div v-else class="news-card-placeholder">
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
<rect width="48" height="48" rx="6" fill="rgba(5,95,194,0.06)" />
<path d="M16 32l6-8 4 5 6-8 6 11H16z" fill="rgba(5,95,194,0.15)" />
<circle cx="20" cy="20" r="3" fill="rgba(5,95,194,0.15)" />
</svg>
</div>
</div>
<div class="news-card-content">
<p class="news-card-title">{{ item.name }}</p>
<div class="news-card-meta">
<span class="news-card-date">{{ item.time }}</span>
<span class="news-card-source">
{{item.sourceName}}
</span>
</div>
</div>
</div>
</template>
<script setup>
defineProps({
item: {
type: Object,
required: true,
},
})
</script>
<style scoped>
.news-card {
width: 398px;
height: 300px;
border-radius: 10px;
background: rgba(255, 255, 255, 1);
border: 1px solid rgba(230, 231, 232, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
overflow: hidden;
position: relative;
cursor: pointer;
transition: box-shadow 0.2s;
}
.news-card:hover {
box-shadow: 0px 0px 24px 0px rgba(25, 69, 130, 0.16);
}
.news-card-image {
position: absolute;
left: 17px;
right: 17px;
top: 15px;
bottom: 105px;
border-radius: 6px;
overflow: hidden;
background: rgba(247, 248, 249, 1);
}
.news-card-image img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.news-card-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, rgba(5, 95, 194, 0.04) 0%, rgba(5, 95, 194, 0.08) 100%);
}
.news-card-content {
position: absolute;
left: 21px;
right: 17px;
bottom: 15px;
height: 78px;
}
.news-card-source{
position: absolute;
right: 0;
right: 0;
top: 0;
font-size: 18px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 700;
line-height: 24px;
color: rgba(59, 65, 75, 1);
text-align: justify;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin: 0;
}
.news-card-title {
position: absolute;
left: 0;
right: 0;
top: 0;
font-size: 18px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 700;
line-height: 24px;
color: rgba(59, 65, 75, 1);
text-align: justify;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin: 0;
}
.news-card-meta {
position: absolute;
left: 0;
right: 28px;
bottom: 0;
display: flex;
justify-content: space-between;
}
.news-card-date {
font-size: 14px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 400;
line-height: 22px;
color: rgba(95, 101, 108, 1);
}
.news-card-source {
font-size: 14px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 400;
line-height: 22px;
color: rgba(95, 101, 108, 1);
position: absolute;
right: -28px;
bottom: 0;
}
</style>
<template>
<div class="news-pagination-bar">
<span class="news-pagination-total">{{ total }}篇新闻报告</span>
<div class="news-pagination">
<button class="pg-btn" :disabled="currentPage <= 1" @click="$emit('update:currentPage', currentPage - 1)">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M10 3l-5 5 5 5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
<template v-for="p in pageList" :key="p">
<span v-if="p === '...'" class="pg-ellipsis">...</span>
<button
v-else
:class="['pg-num', { active: p === currentPage }]"
@click="$emit('update:currentPage', p)"
>{{ p }}</button>
</template>
<button class="pg-btn" :disabled="currentPage >= totalPages" @click="$emit('update:currentPage', currentPage + 1)">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none">
<path d="M6 3l5 5-5 5" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
total: { type: Number, default: 0 },
currentPage: { type: Number, default: 1 },
pageSize: { type: Number, default: 12 },
})
defineEmits(['update:currentPage'])
const totalPages = computed(() => Math.ceil(props.total / props.pageSize) || 1)
const pageList = computed(() => {
const tp = totalPages.value
const cp = props.currentPage
if (tp <= 7) return Array.from({ length: tp }, (_, i) => i + 1)
const pages = []
pages.push(1)
if (cp > 3) pages.push('...')
const start = Math.max(2, cp - 1)
const end = Math.min(tp - 1, cp + 1)
for (let i = start; i <= end; i++) pages.push(i)
if (cp < tp - 2) pages.push('...')
pages.push(tp)
return pages
})
</script>
<style scoped>
.news-pagination-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 0 0 0;
}
.news-pagination-total {
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: 400;
color: rgba(132, 136, 142, 1);
}
.news-pagination {
display: flex;
align-items: center;
gap: 8px;
}
.pg-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 6px;
background: rgba(255, 255, 255, 1);
color: rgba(95, 101, 108, 1);
cursor: pointer;
transition: all 0.15s;
}
.pg-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.pg-num {
min-width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(230, 231, 232, 1);
border-radius: 6px;
background: rgba(255, 255, 255, 1);
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: 400;
color: rgba(59, 65, 75, 1);
cursor: pointer;
transition: all 0.15s;
padding: 0 4px;
}
.pg-num.active {
background: rgba(5, 95, 194, 1);
border-color: rgba(5, 95, 194, 1);
color: #fff;
}
.pg-ellipsis {
font-size: 14px;
color: rgba(132, 136, 142, 1);
width: 32px;
text-align: center;
}
</style>
<template>
<div class="news-sidebar">
<!-- Section: 科技领域 -->
<div class="ns-section">
<div class="ns-section-header">
<span class="ns-header-bar"></span>
<span class="ns-header-title">科技领域</span>
</div>
<div class="ns-options">
<label
v-for="opt in domainOptions"
:key="opt.id"
class="ns-option"
>
<span
:class="['ns-checkbox', { checked: selectedDomains.includes(opt.id) }]"
@click.prevent="$emit('toggle-domain', opt.id)"
>
<svg v-if="selectedDomains.includes(opt.id)" width="14" height="14" viewBox="0 0 14 14" fill="none">
<rect width="14" height="14" rx="4" fill="rgba(5,95,194,1)" />
<path d="M3.5 7l2.5 2.5 4.5-5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" />
</svg>
</span>
<span class="ns-option-label" @click="$emit('toggle-domain', opt.id)">{{ opt.label }}</span>
</label>
</div>
</div>
<div class="ns-section">
<div class="ns-section-header">
<span class="ns-header-bar"></span>
<span class="ns-header-title">发布时间</span>
</div>
<div class="ns-options">
<label
v-for="opt in timeOptions"
:key="opt.id"
class="ns-option"
>
<span
:class="['ns-checkbox', { checked: selectedTimes.includes(opt.id) }]"
@click.prevent="$emit('toggle-time', opt.id)"
>
<svg v-if="selectedTimes.includes(opt.id)" width="14" height="14" viewBox="0 0 14 14" fill="none">
<rect width="14" height="14" rx="4" fill="rgba(5,95,194,1)" />
<path d="M3.5 7l2.5 2.5 4.5-5" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" />
</svg>
</span>
<span class="ns-option-label" @click="$emit('toggle-time', opt.id)">{{ opt.label }}</span>
</label>
</div>
</div>
</div>
</template>
<script setup>
defineProps({
domainOptions: { type: Array, default: () => [] },
timeOptions: { type: Array, default: () => [] },
selectedDomains: { type: Array, default: () => ['all'] },
selectedTimes: { type: Array, default: () => ['all'] },
})
defineEmits(['toggle-domain', 'toggle-time'])
</script>
<style scoped>
.news-sidebar {
width: 360px;
flex-shrink: 0;
background: rgba(255, 255, 255, 1);
border-radius: 10px;
border: 1px solid rgba(234, 236, 238, 1);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
padding: 19px 0 24px 0;
display: flex;
flex-direction: column;
gap: 16px;
overflow: hidden;
}
.ns-section {
display: flex;
flex-direction: column;
gap: 12px;
}
.ns-section-header {
display: flex;
flex-direction: row;
gap: 17px;
align-items: center;
}
.ns-header-bar {
width: 8px;
height: 16px;
background: rgba(5, 95, 194, 1);
border-radius: 0 4px 4px 0;
flex-shrink: 0;
}
.ns-header-title {
font-size: 16px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 700;
letter-spacing: 1px;
line-height: 24px;
color: rgba(5, 95, 194, 1);
}
.ns-options {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 8px 4px;
padding: 0 0 0 24px;
}
.ns-option {
width: 160px;
display: flex;
flex-direction: row;
gap: 8px;
align-items: center;
cursor: pointer;
}
.ns-checkbox {
width: 14px;
height: 14px;
flex-shrink: 0;
border-radius: 4px;
border: 1px solid rgba(230, 231, 232, 1);
display: flex;
align-items: center;
justify-content: center;
background: transparent;
}
.ns-checkbox.checked {
border: none;
}
.ns-checkbox.checked svg {
display: block;
}
.ns-option-label {
font-size: 16px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 400;
line-height: 24px;
color: rgba(95, 101, 108, 1);
user-select: none;
}
</style>
<template>
<div class="news-topbar">
<div class="news-search-wrapper">
<div class="news-search-box">
<input
type="text"
class="news-search-input"
:placeholder="'搜索新闻动态'"
:value="searchText"
@input="$emit('update:searchText', $event.target.value)"
/>
<svg class="news-search-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
<circle cx="7" cy="7" r="5.5" stroke="rgba(132,136,142,1)" stroke-width="1.2" />
<line x1="11" y1="11" x2="14" y2="14" stroke="rgba(132,136,142,1)" stroke-width="1.2" stroke-linecap="round" />
</svg>
</div>
</div>
<div class="news-tabs">
<button
:class="['news-tab', { active: activeTab === 'local' }]"
@click="$emit('update:activeTab', 'local')"
>智库报告</button>
<button
:class="['news-tab', { active: activeTab === 'capital' }]"
@click="$emit('update:activeTab', 'capital')"
>调查项目</button>
</div>
<div class="news-sort" ref="sortDropdownRef">
<button class="news-sort-btn" @click="showSortDropdown = !showSortDropdown">
<svg class="news-sort-icon" width="16" height="16" viewBox="0 0 16 16" fill="none">
<rect x="4" y="3" width="2" height="10" rx="1" fill="rgba(95,101,108,1)" />
<rect x="10" y="6" width="2" height="7" rx="1" fill="rgba(95,101,108,1)" />
</svg>
<span class="news-sort-label">{{ currentSortLabel }}</span>
<svg class="news-sort-arrow" :class="{ open: showSortDropdown }" width="11" height="6" viewBox="0 0 11 6" fill="none">
<path d="M1 1l4.5 4L10 1" stroke="rgba(95,101,108,1)" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</button>
<div v-if="showSortDropdown" class="news-sort-dropdown">
<button
v-for="opt in sortOptions"
:key="opt.value"
@click="selectSort(opt.value)"
class="news-sort-option"
:class="{ active: sortBy === opt.value }"
>{{ opt.label }}</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
const props = defineProps({
searchText: { type: String, default: '' },
activeTab: { type: String, default: 'local' },
sortBy: { type: String, default: 'publishTimeDesc' },
sortOptions: {
type: Array,
default: () => [
{ value: 'publishTimeDesc', label: '发布时间倒序' },
{ value: 'publishTimeAsc', label: '发布时间正序' },
],
},
})
const emit = defineEmits(['update:searchText', 'update:activeTab', 'update:sort'])
const showSortDropdown = ref(false)
const sortDropdownRef = ref(null)
const currentSortLabel = computed(() => {
const opt = props.sortOptions.find((o) => o.value === props.sortBy)
return opt ? opt.label : '发布时间倒序'
})
function selectSort(value) {
emit('update:sort', value)
showSortDropdown.value = false
}
function handleClickOutside(e) {
if (sortDropdownRef.value && !sortDropdownRef.value.contains(e.target)) {
showSortDropdown.value = false
}
}
onMounted(() => document.addEventListener('click', handleClickOutside))
onUnmounted(() => document.removeEventListener('click', handleClickOutside))
</script>
<style scoped>
.news-topbar {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
height: 32px;
margin-bottom: 16px;
}
.news-search-wrapper {
width: 360px;
height: 32px;
flex-shrink: 0;
}
.news-search-box {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
padding: 5px 8px 5px 12px;
border-radius: 4px;
border: 1px solid rgba(230, 231, 232, 1);
background: rgba(255, 255, 255, 1);
}
.news-search-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: 400;
line-height: 22px;
color: rgba(59, 65, 75, 1);
}
.news-search-input::placeholder {
color: rgba(132, 136, 142, 1);
}
.news-search-icon {
flex-shrink: 0;
}
.news-tabs {
display: flex;
flex-direction: row;
gap: 8px;
justify-content: center;
align-items: flex-start;
}
.news-tab {
width: 120px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
padding: 4px 24px;
border-radius: 21px;
font-size: 16px;
font-family: 'Source Han Sans CN', sans-serif;
font-weight: 400;
line-height: 24px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid rgba(234, 236, 238, 1);
background: rgba(255, 255, 255, 1);
color: rgba(95, 101, 108, 1);
}
.news-tab.active {
background: rgba(5, 95, 194, 1);
border-color: rgba(5, 95, 194, 1);
color: rgba(255, 255, 255, 1);
font-weight: 700;
}
.news-sort {
margin-left: auto;
position: relative;
flex-shrink: 0;
}
.news-sort-btn {
width: 120px;
height: 32px;
display: flex;
align-items: center;
padding: 0 12px 0 7px;
gap: 0;
border-radius: 4px;
border: 1px solid rgba(230, 231, 232, 1);
background: rgba(255, 255, 255, 1);
cursor: pointer;
box-sizing: border-box;
}
.news-sort-btn:hover {
border-color: rgba(5, 95, 194, 0.4);
}
.news-sort-icon {
flex-shrink: 0;
margin-right: 6px;
}
.news-sort-label {
flex: 1;
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
font-weight: 400;
color: rgba(95, 101, 108, 1);
white-space: nowrap;
}
.news-sort-arrow {
flex-shrink: 0;
transition: transform 0.2s;
}
.news-sort-arrow.open {
transform: rotate(180deg);
}
.news-sort-dropdown {
position: absolute;
right: 0;
top: 100%;
margin-top: 4px;
min-width: 120px;
background: #fff;
border: 1px solid rgba(234, 236, 238, 1);
border-radius: 8px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
padding: 4px 0;
z-index: 20;
}
.news-sort-option {
display: block;
width: 100%;
text-align: left;
padding: 8px 12px;
font-size: 14px;
font-family: 'Microsoft YaHei', sans-serif;
background: none;
border: none;
cursor: pointer;
color: rgba(95, 101, 108, 1);
}
.news-sort-option:hover {
background: rgba(247, 248, 249, 1);
}
.news-sort-option.active {
color: rgba(5, 95, 194, 1);
font-weight: 500;
}
</style>
<template>
<div class="news-tracker">
<NewsTopBar
v-model:searchText="searchText"
v-model:activeTab="activeTab"
:sort-by="sortBy"
@update:sort="updateSort"
/>
<div class="news-body">
<NewsSidebar
:domain-options="domainOptions"
:time-options="timeOptions"
:selected-domains="selectedDomains"
:selected-times="selectedTimes"
@toggle-domain="toggleDomain"
@toggle-time="toggleTime"
/>
<div class="news-main">
<div class="news-grid">
<NewsCard
v-for="item in newsList"
:key="item.reportId"
:item="item"
/>
</div>
<NewsPagination
:total="totalNews"
v-model:current-page="currentPage"
:page-size="pageSize"
/>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from 'vue'
import { useRoute } from 'vue-router'
import NewsTopBar from './NewsTopBar.vue'
import NewsSidebar from './NewsSidebar.vue'
import NewsCard from './NewsCard.vue'
import NewsPagination from './NewsPagination.vue'
import { getIndustryKeyList } from '@/api/bill/billHome.js'
import { getFindingsReport } from '@/api/characterPage/characterPage.js'
const route = useRoute()
const personId = computed(() => route.params.personId || route.query.personId || '')
const searchText = ref('')
const activeTab = ref('local')
const currentPage = ref(1)
const pageSize = 12
const totalNews = ref(0)
const newsList = ref([])
const loading = ref(false)
const sortBy = ref('publishTimeDesc')
const domainOptions = ref([])
const timeOptions = ref([])
const selectedDomains = ref(['all'])
const selectedTimes = ref(['all'])
async function loadFilterOptions() {
const res = await getIndustryKeyList()
if (res.code === 200 && res.data) {
domainOptions.value = [
{ id: 'all', label: '全部领域' },
...res.data.map(item => ({ id: item.id, label: item.name })),
]
}
const currentYear = new Date().getFullYear()
timeOptions.value = [
{ id: 'all', label: '全部时间' },
...Array.from({ length: 5 }, (_, i) => {
const year = currentYear - i
return { id: String(year), label: `${year}年` }
}),
]
}
function buildParams() {
const params = {
currentPage: currentPage.value - 1,
pageSize,
sortFun: sortBy.value === 'publishTimeAsc',
}
if (!selectedDomains.value.includes('all')) {
params.industryIds = selectedDomains.value
}
if (!selectedTimes.value.includes('all')) {
params.years = selectedTimes.value
}
return params
}
function updateSort(value) {
sortBy.value = value
currentPage.value = 1
loadNews()
}
async function loadNews() {
if (!personId.value) return
loading.value = true
try {
const params = buildParams()
const res = await getFindingsReport(personId.value, params)
if (res.code === 200 && res.data) {
newsList.value = res.data.content || []
totalNews.value = res.data.totalElements || 0
} else {
newsList.value = []
totalNews.value = 0
}
} finally {
loading.value = false
}
}
function toggleDomain(id) {
if (id === 'all') {
selectedDomains.value = ['all']
} else {
const without = selectedDomains.value.filter(d => d !== 'all')
const idx = without.indexOf(id)
if (idx > -1) without.splice(idx, 1)
else without.push(id)
selectedDomains.value = without.length > 0 ? without : ['all']
}
}
function toggleTime(id) {
if (id === 'all') {
selectedTimes.value = ['all']
} else {
const without = selectedTimes.value.filter(d => d !== 'all')
const idx = without.indexOf(id)
if (idx > -1) without.splice(idx, 1)
else without.push(id)
selectedTimes.value = without.length > 0 ? without : ['all']
}
}
watch(
() => [selectedDomains.value, selectedTimes.value],
() => {
currentPage.value = 1
loadNews()
},
{ deep: true }
)
watch(currentPage, () => {
loadNews()
})
onMounted(async () => {
await loadFilterOptions()
await loadNews()
})
</script>
<style scoped>
.news-tracker {
width: 100%;
background: rgba(247, 248, 249, 1);
padding: 0;
}
.news-body {
display: flex;
flex-direction: row;
gap: 14px;
align-items: flex-start;
}
.news-main {
flex: 1;
min-width: 0;
}
.news-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 16px;
align-content: flex-start;
align-items: flex-start;
}
</style>
<template>
<div class="think-tank-person">
<!-- 智库研究人员人物基础 -->
<div class="header">
<div class="avatar">
<!-- <img :src="characterInfo.imageUrl" alt="" /> -->
<el-avatar :size="160" shape="circle" :src="characterInfo.imageUrl" />
</div>
<div class="info">
<!-- <div class="name"> -->
<p class="name-cn">{{ characterInfo.name }}</p>
<p class="name-en">{{ characterInfo.ename }}</p>
<!-- </div> -->
<div class="introduction">
<p>{{ characterInfo.description }}</p>
</div>
<div class="domain">
<p
v-for="item in characterInfo.industryList"
:key="item"
class="cl1"
:class="{
cl1: item.status === '1',
cl2: item.status === '2',
cl3: item.status === '3',
cl4: item.status === '4',
cl5: item.status === '5',
cl6: item.status === '6'
}"
>
{{ item.industryName }}
</p>
<AreaTag v-for="item in characterInfo.industryList" :key="item.industryId"
:tag-name="item.industryName" />
</div>
</div>
</div>
<!-- 智库研究人员人物信息区分 -->
<div class="info-divide">
<div v-for="item in info" :key="item" :class="{ active: item === infoActive }" @click="infoActive = item">
{{ item }}
</div>
</div>
<!-- 人物详情 -->
<div class="info-content" v-if="infoActive === '人物详情'">
<div class="info-content" v-show="infoActive === '人物详情'">
<div class="left">
<div class="left-top">
<div class="title">
<div class="box"></div>
<div class="text">核心观点</div>
<div class="btn">
<img src="./assets/下载按钮.png" alt="" />
<img src="./assets/收藏按钮.png" alt="" />
<AnalysisBox title="核心观点" width="1064px" height="300px" :show-all-btn="false" class="left-top">
<template #headerBtn>
<el-select v-model="numActive" class="tab-select" :teleported="true" @change="handleChangeYear">
<el-option label="全部" value="全部" />
<el-option v-for="item in num" :key="item" :label="item" :value="item" />
</el-select>
</template>
<div class="echarts" id="wordCloudChart"></div>
</AnalysisBox>
<AnalysisBox title="最新动态" width="1064px" height="1617px" :show-all-btn="false" class="left-bottom">
<template #headerBtn>
<div class="input"><input type="checkbox" v-model="isChecked" @change="handleChange" />只看涉华动态
</div>
</div>
<!-- 主要内容 -->
<div class="num-list">
<div v-for="item in num" :key="item" :class="{ active: item === numActive }" @click="handleChangeYear(item)">
{{ item }}
</div>
</div>
<!-- echarts 图表 -->
<div class="echarts" id="wordCloudChart">
<!-- <div class="row" v-for="(row, index) in wordCloudData" :key="index">
<span
v-for="(item, idx) in row"
:key="idx"
:style="{
color: item.color,
fontSize: item.fontSize || '16px',
fontWeight: item.fontWeight || 'normal'
}"
>
{{ item.text }}
</span>
</div> -->
</div>
</div>
<div class="left-bottom">
<div class="title">
<div class="box"></div>
<div class="text">最新动态</div>
<div class="input"><input type="checkbox" v-model="isChecked" @change="handleChange"/>只看涉华动态</div>
<div class="btn">
<img src="./assets/下载按钮.png" alt="" />
<img src="./assets/收藏按钮.png" alt="" />
</div>
</div>
<!-- 主要内容 -->
</template>
<div class="main">
<div v-for="item in CharacterLatestDynamic" :key="item" class="main-item">
<div class="time">
<div class="year">{{ item.time.split("-")[0] }}</div>
<div class="date">{{ item.time.split("-")[1] + "月" + item.time.split("-")[2] + "日"}}</div>
<div class="date">{{ item.time.split("-")[1] + "月" + item.time.split("-")[2] + "日" }}
</div>
</div>
<div class="image">
<img src="./assets/type1.png" alt="" v-if="item.remarks === true" /><img
src="./assets/type2.png"
alt=""
v-else
/>
src="./assets/type2.png" alt="" v-else />
</div>
<div class="content">
<div :class="{ 'content-type1': item.remarks === true, 'content-type2': item.remarks === false }">
<p v-if="item.remarks === true" class="content-title1">
<div
:class="{ 'content-type1': item.remarks === true, 'content-type2': item.remarks === false }">
<p v-if="item.remarks === true" class="content-title1">
{{ item.content }}
</p>
<p v-else class="content-title2">
<p v-else class="content-title2">
{{ item.title }}
</p>
<p v-if="item.remarks === true" class="content-title-en">{{ item.econtent }}</p>
</div>
<p v-if="item.remarks === false" class="content-contentcontent">{{ item.content }}</p>
<div class="content-tag">
<div>
<span
v-for="tag in item.industryList"
:key="tag"
class="tag"
:class="{
dl1: tag === '人工智能',
dl2: tag === '量子科技',
dl3: tag === '新能源',
dl4: tag === '集成电路'
}"
@click="handleClickTag(tag)"
>{{ tag }}</span
>
<div style="display: flex; flex-wrap: wrap; gap: 8px;">
<AreaTag v-for="tag in item.industryList" :key="tag" :tag-name="tag.industryName"
@click="handleClickTag(tag.industryName)" />
</div>
<div class="origin">来源:{{ item.orgName }}</div>
</div>
......@@ -133,69 +72,58 @@
<div class="line-test"></div>
</div>
<div class="pagination">
<div class="total">{{`共 ${total} 项`}}</div>
<el-pagination
@current-change="handleCurrentChange"
:page-size="pageSize"
:current-page="currentPage"
background
layout="prev, pager, next"
:total="total"
class="custom-pagination"
/>
<div class="total">{{ `共 ${total} 项` }}</div>
<el-pagination @current-change="handleCurrentChange" :page-size="pageSize"
:current-page="currentPage" background layout="prev, pager, next" :total="total"
class="custom-pagination" />
</div>
</div>
</AnalysisBox>
</div>
<div class="right">
<div class="right-top">
<div class="title">
<div class="box"></div>
<div class="text">基本信息</div>
<div class="btn">
<img src="./assets/下载按钮.png" alt="" />
<img src="./assets/收藏按钮.png" alt="" />
</div>
</div>
<div class="right" >
<AnalysisBox title="基本信息" width="520px" :height="boxHeight" :show-all-btn="false" class="right-top" v-if="characterBasicInfo ">
<div class="main-content">
<div class="baseInfo">
<div class="baseInfo-item">
<div class="baseInfo-item-title">出生日期:</div>
<div class="baseInfo-item-content">{{characterBasicInfo.birthday}}</div>
<div class="baseInfo-item-content">{{ characterBasicInfo.birthday }}</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">现任职位:</div>
<div class="baseInfo-item-content">{{characterBasicInfo.positionTitle}}</div>
<div class="baseInfo-item-content">{{ characterBasicInfo.positionTitle }}</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">兼任职位:</div>
<div class="baseInfo-item-content">{{characterBasicInfo.sideJob}}</div>
<div class="baseInfo-item-content">{{ characterBasicInfo.sideJob }}</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">政策倾向:</div>
<div class="baseInfo-item-content">{{characterBasicInfo.political}}</div>
<div class="baseInfo-item-content">{{ characterBasicInfo.political }}</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">国籍:</div>
<div class="baseInfo-item-content">{{characterBasicInfo.country}}</div>
<div class="baseInfo-item-content">{{ characterBasicInfo.country }}</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">教育背景:</div>
<div class="baseInfo-item-content long" v-for="item in characterBasicInfo.educationList">
{{ item.school + '--' + item.major}}<br>
<div class="baseInfo-item-content long"
v-for="item in characterBasicInfo.educationList">
{{ item.school + '--' + item.major }}<br>
</div>
</div>
<div class="baseInfo-item">
<div class="baseInfo-item-title">研究领域:</div>
<div class="baseInfo-item-content longlong">
<span class="span" v-for="item in characterBasicInfo.industryList">{{item.industryName}}</span>
<span class="span"
v-for="item in characterBasicInfo.industryList">{{ item.industryName }}</span>
</div>
</div>
</div>
<div class="company">
<div class="company-title">社交媒体</div>
<div class="company-content">
<div v-for="item in characterBasicInfo.organizationList" :key="item" class="company-item">
<img :src="item.imageUrl?item.imageUrl:DefaultIcon2" alt="" />
<div v-for="item in characterBasicInfo.organizationList" :key="item"
class="company-item">
<img :src="item.imageUrl ? item.imageUrl : DefaultIcon2" alt="" />
<div>
<div class="company-cn">{{ item.name }}</div>
<div class="company-name">{{ item.ename }}</div>
......@@ -204,46 +132,15 @@
</div>
</div>
</div>
</div>
<div class="right-bottom">
<div class="title">
<div class="box"></div>
<div class="text">政治履历</div>
<div class="btn">
<img src="./assets/下载按钮.png" alt="" />
<img src="./assets/收藏按钮.png" alt="" />
</div>
</div>
<div class="content-main">
<div v-for="item in CharacterResume" class="content-item">
<img src="./assets/icon01.png" alt="" class="image01" />
<div class="content-item-time">{{ item.startTime +'-' + item.endTime}}</div>
<div class="content-item-title">{{ item.orgName +'|' + item.jobName}}</div>
<div class="content-item-content">{{ item.content }}</div>
<div class="content-item-door" v-if="item.door">
<img src="./assets/icon02.png" alt="" />
<span>{{ item.door }}</span>
</div>
</div>
</div>
</div>
</AnalysisBox>
<ResumeCard title="政治履历" :list="CharacterResume" @download="handleDownload" @collect="handleCollect" />
</div>
</div>
<!-- 成果报告 -->
<HistoricalProposal v-if="infoActive === '成果报告'" />
<!-- 人物关系 -->
<CharacterRelationships v-if="infoActive === '人物关系'" />
<!-- 相关情况 -->
<RelevantSituation v-if="infoActive === '相关情况'" />
<!-- 弹框 -->
<el-dialog
v-model="dialogVisible"
width="761px"
class="viewpoint-dialog"
:modal="false"
:draggable="true"
:lock-scroll="false"
>
<el-dialog v-model="dialogVisible" width="761px" class="viewpoint-dialog" :modal="false" :draggable="true"
:lock-scroll="false">
<template #header>
<div class="viewpoint-header">
<div class="viewpoint-title">领域观点</div>
......@@ -265,23 +162,27 @@
</template>
<script setup>
import { ref, onMounted } from "vue";
import { ref, onMounted,computed } from "vue";
import setChart from "@/utils/setChart";
import CharacterRelationships from "./components/characterRelationships/index.vue";
import RelevantSituation from "./components/relevantSituation/index.vue";
import HistoricalProposal from "./components/historicalProposal/index.vue";
import HistoricalProposal from "./components/historicalProposal/components/NewsTracker.vue";
import getWordCloudChart from "../../utils/worldCloudChart";
import { getCharacterGlobalInfo,
import {
getCharacterGlobalInfo,
getCharacterBasicInfo,
getCharacterView,
getCharacterLatestDynamic,
getCharacterResume,
getCharacterFieldView } from "@/api/characterPage/characterPage.js";
getCharacterFieldView
} from "@/api/characterPage/characterPage.js";
import Musk from "./assets/Musk.png";
import cp1 from "./assets/cp1.png";
import cp2 from "./assets/cp2.png";
import ResumeCard from "./components/Resume.vue";
import AnalysisBox from '@/components/base/boxBackground/analysisBox.vue';
import AreaTag from '@/components/base/AreaTag/index.vue';
import img1 from "./assets/img1.png";
import img2 from "./assets/img2.png";
......@@ -295,30 +196,29 @@ import DefaultIcon2 from '@/assets/icons/default-icon2.png'
import { useRoute } from 'vue-router';
const route = useRoute();
const personId = ref(route.query.personId || "Y000064");
// 处理图片代理
const boxHeight = computed(() => {
if(characterBasicInfo.value.organizationList==undefined) return '625px'
if(characterBasicInfo.value.organizationList.length==0) return '425px'
if(characterBasicInfo.value.organizationList.length<=2) return '525px'
return '625px'
})
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 characterInfo = ref({});
const getCharacterGlobalInfoFn = async () => {
const params = {
personId: personId.value
};
try{
try {
const res = await getCharacterGlobalInfo(params);
if (res.code === 200) {
console.log("人物全局信息", res);
......@@ -326,19 +226,18 @@ const getCharacterGlobalInfoFn = async () => {
characterInfo.value = res.data;
}
}
}catch(error){
} catch (error) {
}
};
// 获取人物基本信息
const characterBasicInfo = ref({});
const getCharacterBasicInfoFn = async () => {
const params = {
personId: personId.value
};
try{
try {
const res = await getCharacterBasicInfo(params);
if (res.code === 200) {
console.log("人物基本信息", res);
......@@ -346,35 +245,37 @@ const getCharacterBasicInfoFn = async () => {
characterBasicInfo.value = res.data;
}
}
}catch (error) {
} catch (error) {
console.error(error);
}
};
// 获取人物观点
const characterView = ref({});
const getCharacterViewFn = async () => {
const params = {
personId: personId.value,
year: numActive.value
};
try{
if(numActive.value!='全部'){
params['year']=numActive.value
}
try {
const res = await getCharacterView(params);
if (res.code === 200) {
console.log("人物观点", res);
if (res.data) {
characterView.value = res.data.map(item=>{
return{
name:item.option,
value:item.count
characterView.value = res.data.map(item => {
return {
name: item.option,
value: item.count
};
});
}
}
}catch(error){
} catch (error) {
}
};
const handleCharacterView = async () => {
......@@ -383,19 +284,18 @@ const handleCharacterView = async () => {
setChart(wordCloudChart, "wordCloudChart");
};
const handleChangeYear = (item) => {
numActive.value = item;
characterView.value = [];
handleCharacterView();
const handleChangeYear = (value) => {
numActive.value = value;
characterView.value = [];
handleCharacterView();
};
// 获取领域观点
const CharacterFieldView = ref({});
const getCharacterFieldViewFn = async () => {
const params = {
areaId: window.sessionStorage.getItem("areaId") || "20",
};
try{
try {
const res = await getCharacterFieldView(params);
if (res.code === 200) {
console.log("领域观点", res);
......@@ -403,20 +303,19 @@ const getCharacterFieldViewFn = async () => {
CharacterFieldView.value = res.data;
}
}
}catch(error){
} catch (error) {
}
};
//是否涉华
const isChecked = ref(false);
const related = ref('N');
const handleChange = event => {
if(isChecked.value){
if (isChecked.value) {
related.value = 'Y';
}else{
} else {
related.value = 'N';
}
getCharacterLatestDynamicFn();
......@@ -424,40 +323,36 @@ const handleChange = event => {
};
const currentPage = ref(1);
// 处理页码改变事件
const handleCurrentChange = page => {
currentPage.value = page;
getCharacterLatestDynamicFn();
};
// 获取最新动态
const CharacterLatestDynamic = ref({});
const total = ref(0);
const pageSize = ref(7);
const loading = ref(false);
const abortController = ref(null);
const getCharacterLatestDynamicFn = async () => {
// 取消上一次未完成的请求
if (abortController.value) {
abortController.value.abort();
}
// 创建新的 AbortController
abortController.value = new AbortController();
loading.value = true;
const params = {
personId: personId.value,
cRelated: related.value,
cRelated: related.value,
currentPage: currentPage.value - 1,
pageSize: pageSize.value
};
try{
try {
const res = await getCharacterLatestDynamic(params, abortController.value.signal);
console.log("最新动态", res);
if (res.code === 200) {
if (res.data&& res.data.content) {
if (res.data && res.data.content) {
CharacterLatestDynamic.value = res.data.content.map(item => ({
title: item.title,
content: item.content,
......@@ -468,31 +363,30 @@ const getCharacterLatestDynamicFn = async () => {
remarks: item.remarks
}));
total.value = res.data.totalElements;
}else {
} else {
CharacterLatestDynamic.value = [];
total.value = 0;
}
}else {
} else {
CharacterLatestDynamic.value = [];
total.value = 0;
}
loading.value = false;
}catch (error) {
} catch (error) {
if (error.name !== "AbortError") {
console.error(error);
loading.value = false;
}
}
};
//获取职业履历
const CharacterResume = ref({});
const getCharacterResumeFn = async () => {
const params = {
personId: personId.value
};
try{
try {
const res = await getCharacterResume(params);
if (res.code === 200) {
console.log("人物职业履历", res);
......@@ -500,157 +394,38 @@ const getCharacterResumeFn = async () => {
CharacterResume.value = res.data;
}
}
}catch(error){
} catch (error) {
}
};
};
const handleDownload = () => { };
const handleCollect = () => { };
onMounted(() => {
getCharacterGlobalInfoFn();
getCharacterBasicInfoFn();
// getCharacterViewFn();
getCharacterLatestDynamicFn();
getCharacterResumeFn();
getCharacterFieldViewFn();
handleCharacterView();
});
const wordCloudData = ref([
[
{ text: "AI安全与模型保护", color: "rgba(19, 168, 168, 1)" },
{ text: "地球化改造", color: "rgb(206, 79, 81)" },
{ text: "减少燃料对外依赖", color: "rgba(250, 173, 20, 1)" },
{ text: "外交政策立场", color: "rgba(250, 173, 20, 1)" },
{ text: "AI供应链监督", color: "rgba(22, 119, 255, 1)" },
{ text: "预算纪律", color: "rgba(19, 168, 168, 1)" }
],
[
{ text: "外交网络全球野心", color: "rgb(206, 79, 81)" },
{ text: "NATO扩张与乌克兰整合", color: "rgba(22, 119, 255, 1)" },
{ text: "技术扩散威胁", color: "rgba(250, 173, 20, 1)" },
{ text: "盟友安全保障", color: "rgb(206, 79, 81)" },
{ text: "波罗的海战略转变", color: "rgba(250, 173, 20, 1)" },
// { text: "教育政策", color: "rgba(19, 168, 168, 1)" },
{ text: "可持续能源", color: "rgba(255, 122, 69, 1)" }
],
[
{ text: "减少政府支出", color: "rgba(255, 122, 69, 1)" },
{ text: "可持续能源", color: "rgba(255, 122, 69, 1)" },
{ text: "第一性原理", color: "rgba(250, 173, 20, 1)" },
{ text: "AI治理与国际监督", color: "rgba(255, 122, 69, 1)" },
{ text: "经济安全优先", color: "rgb(206, 79, 81)" },
{ text: "有限政府", color: "rgba(255, 122, 69, 1)" }
],
[
{ text: "综合威胁架构", color: "rgba(22, 119, 255, 1)" },
{ text: "AGI重塑全球权力动态", color: "rgb(206, 79, 81)", fontSize: "26px", fontWeight: "700" },
{ text: "不确定性即弱点", color: "rgba(255, 122, 69, 1)" },
// { text: "战后重建需安全基础", color: "rgba(22, 119, 255, 1)" },
{ text: "战后重建需安全基础", color: "rgba(250, 173, 20, 1)" },
{ text: "受控竞争通道", color: "rgba(250, 173, 20, 1)" }
],
[
{ text: "经济-技术激烈竞争", color: "rgba(19, 168, 168, 1)" },
{ text: "印太-跨大西洋双线", color: "rgba(250, 173, 20, 1)" },
{ text: "超音速海啸", color: "rgba(250, 173, 20, 1)" },
{ text: "乌克兰胜利保障", color: "rgba(19, 168, 168, 1)" },
{ text: "平民“联合参谋部”", color: "rgb(206, 79, 81)" },
{ text: "多智能体协作", color: "rgba(19, 168, 168, 1)" }
]
]);
const musk = ref({
nameCn: "巴里·帕维尔",
nameEn: "Barry Pavel",
introduction:
"美国兰德公司国家安全研究部副总裁兼主任,曾任白宫国家安全委员会国防政策高级主任,30余年深耕大国战略竞争与AI地缘政治风险。",
domain: ["跨党派鹰派", "大国竞争", "AI/AGI地缘政治", "综合威慑", "战略预测"],
avatar: Musk
});
const info = ref(["人物详情", "成果报告", "人物关系", "相关情况"]);
const info = ref(["人物详情", "成果报告", "人物关系" ]);
// const info = ref(["人物详情", "成果报告", "人物关系", "相关情况"]);
const infoActive = ref("人物详情");
const num = ref(["2025", "2024", "2023", "2022", "2021", "2020"]);
const numActive = ref("2025");
const numActive = ref("全部");
const dialogVisible = ref(false);
const dialogData = ref([]);
const handleClickTag = tag => {
dialogVisible.value = true;
};
const newList = ref([
{
id: 1,
title: "如果普京拿下整个乌克兰,并在7个NATO成员国边境部署包括核武的部队,美国和欧洲的风险将巨大且持续。这是我们自私的国家利益,防止普京获胜。",
titleEn:
"If Putin takes all of Ukraine, and deploys forces including nuclear wpns on the borders of 7 NATO members, the risks to US and Europe will be enormous and sustained. It’s in our selfish national interests to prevent a Putin win.",
pie: ["人工智能"],
origin: "Linkedln",
time: "2025年10月10日",
type: 1
},
{
id: 2,
title: "欧洲长期战争:2024年及以后美国、波兰及盟友选项",
content:
"兰德国家安全研究部与波兰国际事务研究所于2024年4月25日举办公开活动,讨论为什么2024年是确保乌克兰胜利和遏制俄罗斯的关键转折年。通过长期战略分析和可操作政策建议,美国、波兰及盟友专家概述了如何在乌克兰、欧洲及其他地区有效捍卫基于规则的国际秩序。Barry Pavel作为主讲人之一,强调联盟合作的重要性。",
pie: ["量子科技"],
origin: "兰德公司",
time: "2025年10月10日",
type: 2
},
{
id: 3,
title: "女性视角如何塑造国家安全",
content:
"兰德公司妇女、和平与安全倡议于2024年5月举办活动,探讨性别视角在帮助美国及盟国政府决策者应对最紧迫国家安全挑战中的关键作用。Barry Pavel与Carol M. Pottenger、Deborah Avant等专家共同参与,强调多样化视角在政策制定中的价值。",
pie: ["新能源"],
origin: "兰德公司",
time: "2025年10月09日",
type: 2
},
{
id: 4,
title: "AI安全:保护大型语言模型及其对地缘政治未来的影响",
content:
"兰德公司召集AI和全球安全专家进行专题讨论,聚焦AI安全的日益重要性及其对国家和本土安全的含义。Barry Pavel与Richard Danzig、Jim Mitre等参与,分析保护大型语言模型以应对地缘政治风险的必要性,并探讨其对全球安全的战略影响。",
pie: ["量子科技"],
origin: "兰德公司",
time: "2025年10月09日",
type: 2
},
{
id: 5,
title: "虽然乌克兰短期内不太可能将俄罗斯赶回边境,但战争在未来几个月可能有利于乌克兰。但前提是西方不要先眨眼。",
titleEn:
"Good timely @RANDCorporation analysis on war trajectories. 'Although Ukraine is unlikely to throw Russia back to its borders any time soon, the war will likely trend in Ukraine's favor in the coming months. But only if the West does not blink first.",
pie: ["人工智能"],
origin: "X",
time: "2025年10月08日",
type: 1
},
{
id: 6,
title: "平衡和避免公然挑衅是合理的原则。但系统性地决定“做x”是否会在普京脑海中太挑衅的政策,会让他牢牢掌控局面直到获胜。他可以挑选美国/NATO能做什么。",
titleEn:
"Balance and avoiding blatant provocation are sound principles. But a systematic policy of deciding whether “doing x” might be too provocative in Putin’s mind keeps him firmly in the driver’s seat until he wins. He gets to pick and choose what the US/NATO can do.",
pie: ["集成电路"],
origin: "X",
time: "2025年10月08日",
type: 1
},
{
id: 7,
title: "兰德专家预览2024年北约峰会",
content:
"兰德公司于2024年6月26日举办媒体电话会议,预览即将于7月9-11日举行的华盛顿北约峰会。Barry Pavel作为国家安全研究部副总裁,分享美国国防战略、能力及联盟专长,讨论对乌克兰的支持、联盟防御强化、东部侧翼加固、新成员芬兰和瑞典的整合,以及应对中国挑战和新兴技术等问题。",
pie: ["量子科技"],
origin: "兰德公司",
time: "2025年10月09日",
type: 2
}
]);
const companyList = ref([
{
id: 1,
......@@ -665,92 +440,6 @@ const companyList = ref([
logo: cp2
}
]);
// 政治履历
const resumeList = ref([
{
id: 1,
time: "2022年 – 至今",
title: "兰德公司 | 国家安全研究部副总裁兼主任",
content:
"基于华盛顿办公室,监督国防政策、AI安全与地缘政治研究;于2022年8月8日正式加入。",
door: ""
},
{
id: 2,
time: "2011年 – 2022年",
title: "大西洋理事会 | Scowcroft战略与安全中心创始主任兼高级副总裁",
content: "专注地缘政治、新兴安全挑战、美国联盟适应与国防战略调整;服务11年。2022年后转为杰出研究员。",
door: ""
},
{
id: 3,
time: "2008年 – 2010年",
title: "白宫国家安全委员会 | 总统特别助理兼国防政策与战略高级主任",
content:
"服务乔治·W·布什与巴拉克·奥巴马两届政府,作为职业公务员;领导首份国家安全优先审查,并贡献2010年国家安全战略。",
door: ""
},
{
id: 4,
time: "2004年 – 2006年",
title: "美国国防部 | 代理国防部副助理部长",
content:
"负责战略与政策规划,为总统的政策制定提供理论支撑与咨询。",
door: ""
},
{
id: 5,
time: "1999年 – 2001年",
title: "外交关系委员会 | 任期成员咨询委员会联合主席",
content: "参与外交政策咨询与网络构建。",
door: ""
},
{
id: 6,
time: "1991年 – 2004年",
title: "美国国防部 | 国防部政策副部长办公室成员",
content: "角色包括战略规划与政策支持。",
door: ""
}
]);
// 弹框数据
const dialogData = ref([
{
id: 1,
name: "山姆・奥特曼",
content: "主张分级监管,反对 “一刀切” 审批;警告美国领先优势有限,电力与基建是关键瓶颈。",
img: img1,
job: "OpenAI CEO"
},
{
id: 2,
name: "布拉德・史密斯",
content: "主张全技术栈监管与基础建设投入,强调美国需在芯片、算法、数据各层保持领先。",
img: img2,
job: "微软副董事长"
},
{
id: 3,
name: "黄仁勋",
content: "认为 AI 处于 “智能基建起步期”,大模型为基础,代理式 AI 为下一阶段;否定 AI 泡沫,强调与实体经济融合。",
img: img3,
job: "NVIDIA CEO"
},
{
id: 4,
name: "杰弗里・辛顿",
content: "担忧 AI 发展过快,警告 “涌现能力” 超预期,可能导致存在性风险;呼吁放缓研发、加强安全。",
img: img4,
job: "图灵奖得主、深度学习先驱"
},
{
id: 5,
name: "李飞飞",
content: "关注 AI 伦理与公平性,强调安全与普惠并重,对 AGI 时间表持谨慎态度。",
img: img5,
job: "斯坦福大学教授"
}
]);
</script>
<style lang="scss" scoped>
......@@ -758,10 +447,14 @@ const dialogData = ref([
margin: 0;
padding: 0;
}
:deep(.wrapper-main){
margin-top: 15px;
}
.think-tank-person {
width: 1600px;
margin: 0 auto;
padding-bottom: 50px;
.header {
width: 1600px;
height: 200px;
......@@ -772,19 +465,23 @@ const dialogData = ref([
padding: 20px;
display: flex;
align-items: center;
.avatar {
width: 160px;
height: 160px;
margin-right: 24px;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
.info {
flex: 1;
.name-cn {
font-size: 32px;
font-weight: 700;
......@@ -793,6 +490,7 @@ const dialogData = ref([
color: rgb(59, 65, 75);
margin-bottom: 8px;
}
.name-en {
font-size: 16px;
font-weight: 400;
......@@ -801,6 +499,7 @@ const dialogData = ref([
color: rgb(59, 65, 75);
margin-bottom: 6px;
}
.introduction {
font-size: 16px;
font-weight: 400;
......@@ -809,48 +508,15 @@ const dialogData = ref([
color: rgb(59, 65, 75);
margin-bottom: 6px;
}
.domain {
font-size: 14px;
p {
display: inline-block;
padding: 1px 8px;
border-radius: 4px;
margin-right: 8px;
border: 1px solid;
}
.cl1 {
border-color: rgba(186, 224, 255, 1);
background-color: rgba(230, 244, 255, 1);
color: rgba(22, 119, 255, 1);
}
.cl2 {
border-color: rgba(217, 247, 190, 1);
background-color: rgba(246, 255, 237, 1);
color: rgba(82, 196, 26, 1);
}
.cl3 {
border-color: rgba(255, 241, 184, 1);
background-color: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
}
.cl4 {
border-color: rgba(255, 204, 199, 1);
background-color: rgba(255, 241, 240, 1);
color: rgba(255, 77, 79, 1);
}
.cl5 {
border-color: rgba(255, 241, 184, 1);
background-color: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
}
.cl6 {
border-color: rgba(255, 204, 199, 1);
background-color: rgba(255, 241, 240, 1);
color: rgba(255, 77, 79, 1);
}
display: flex;
flex-wrap: wrap;
gap: 8px;
}
}
}
.info-divide {
width: 1600px;
height: 64px;
......@@ -862,6 +528,7 @@ const dialogData = ref([
display: flex;
justify-content: space-between;
align-items: center;
div {
width: 530px;
height: 54px;
......@@ -873,10 +540,12 @@ const dialogData = ref([
color: rgb(59, 65, 75);
border-radius: 10px;
cursor: pointer;
&:hover {
background: rgba(231, 243, 255, 1);
}
}
.active {
font-size: 24px;
font-weight: 700;
......@@ -887,655 +556,377 @@ const dialogData = ref([
border: 2px solid rgba(174, 214, 255, 1);
}
}
.info-content {
width: 1600px;
height: 1933px;
margin-bottom: 50px;
margin: 16px auto;
display: flex;
.left {
width: 1064px;
height: 100%;
margin-right: 16px;
.left-top {
width: 100%;
height: 300px;
margin-bottom: 16px;
background-color: rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
.title {
width: 100%;
height: 56px;
display: flex;
align-items: center;
padding: 14px 12px 16px 0;
.box {
width: 8px;
height: 20px;
background-color: rgb(5, 95, 194);
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
margin-right: 14px;
}
.text {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.btn {
width: 60px;
height: 28px;
margin-left: auto;
img {
width: 28px;
height: 28px;
cursor: pointer;
}
img:first-child {
margin-right: 4px;
}
}
}
.num-list {
display: flex;
align-items: center;
padding-left: 24px;
margin-bottom: 16px;
div {
padding: 1px 8px;
line-height: 30px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
cursor: pointer;
border: 1px solid rgb(230, 231, 232);
border-radius: 4px;
margin-right: 8px;
}
.active {
background-color: rgba(231, 243, 255, 1);
color: rgb(5, 95, 194);
border-color: rgb(5, 95, 194);
}
}
.echarts {
width: 1009px;
height: 170px;
margin-left: 24px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 10px 0;
}
.row {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
span {
font-family: "Microsoft YaHei";
white-space: nowrap;
cursor: pointer;
transition: all 0.3s;
.echarts {
width: 100%;
height: 200px;
box-sizing: border-box;
}
&:hover {
transform: scale(1.1);
text-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}
}
}
.input {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(132, 136, 142);
input {
margin-right: 8px;
}
}
.left-bottom {
width: 100%;
height: 1617px;
background-color: rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
.title {
.main {
width: 1064px;
height: 1435px;
box-sizing: border-box;
padding: 0 50px 0 24px;
position: relative;
z-index: 110;
.main-item {
width: 100%;
height: 85px;
margin-bottom: 40px;
display: flex;
align-items: center;
padding: 14px 12px 45px 0;
position: relative;
.box {
width: 8px;
height: 20px;
background-color: rgb(5, 95, 194);
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
margin-right: 14px;
}
.text {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.btn {
width: 60px;
height: 28px;
margin-left: auto;
img {
width: 28px;
height: 28px;
cursor: pointer;
.time {
width: 77px;
box-sizing: border-box;
margin-right: 20px;
display: flex;
flex-direction: column;
align-items: flex-end;
.year {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
line-height: 24px;
}
img:first-child {
margin-right: 4px;
.date {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
line-height: 24px;
}
}
.input {
position: absolute;
top: 15px;
right: 114px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(132, 136, 142);
input {
margin-right: 8px;
.image {
margin-right: 20px;
img {
width: 24px;
height: 24px;
}
}
}
.main {
width: 1064px;
height: 1435px;
box-sizing: border-box;
padding-right: 50px;
position: relative;
z-index: 110;
.main-item {
width: 1014px;
margin-bottom: 40px;
display: flex;
.time {
width: 77px;
box-sizing: border-box;
margin-right: 20px;
display: flex;
flex-direction: column;
align-items: flex-end;
.year {
.content {
flex: 1;
.content-type1 {
background-color: rgba(246, 250, 255, 1);
border-radius: 10px;
border: 1px solid rgb(234, 236, 238);
padding: 12px 14px 12px 15px;
.content-title1 {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
color: rgb(59, 65, 75);
line-height: 24px;
padding-bottom: 12px;
border-bottom: 1px solid rgb(234, 236, 238);
cursor: pointer;
}
.date {
.content-title-en {
font-size: 16px;
font-weight: 700;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(5, 95, 194);
color: rgb(59, 65, 75);
line-height: 24px;
padding-top: 12px;
cursor: pointer;
}
}
.image {
margin-right: 20px;
img {
width: 24px;
height: 24px;
.content-type2 {
.content-title2 {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
line-height: 26px;
padding-bottom: 8px;
cursor: pointer;
}
}
.content {
width: 873px;
.content-type1 {
background-color: rgba(246, 250, 255, 1);
border-radius: 10px;
border: 1px solid rgb(234, 236, 238);
padding: 12px 14px 12px 15px;
.content-title1 {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
line-height: 24px;
padding-bottom: 12px;
border-bottom: 1px solid rgb(234, 236, 238);
cursor: pointer;
}
.content-title-en {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
line-height: 24px;
padding-top: 12px;
cursor: pointer;
}
}
.content-type2 {
.content-title2 {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
line-height: 26px;
padding-bottom: 8px;
cursor: pointer;
}
}
.content-contentcontent {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
font-size: 16px;
.content-contentcontent {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
text-overflow: ellipsis;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(95, 101, 108);
line-height: 24px;
margin-bottom: 8px;
cursor: pointer;
}
.content-tag {
width: 100%;
display: flex;
justify-content: space-between;
.origin {
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(95, 101, 108);
line-height: 24px;
margin-bottom: 8px;
line-height: 22px;
color: rgb(132, 136, 142);
cursor: pointer;
}
.content-tag {
width: 873px;
display: flex;
justify-content: space-between;
.tag {
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
margin-right: 8px;
padding: 1px 10px;
border-radius: 4px;
border: 1px solid;
cursor: pointer;
}
.dl1 {
background-color: rgba(230, 255, 251, 1);
color: rgba(19, 168, 168, 1);
border-color: rgba(135, 232, 222, 1);
}
.dl2 {
background-color: rgba(255, 241, 240, 1);
color: rgb(206, 79, 81);
border-color: rgba(255, 163, 158, 1);
}
.dl3 {
background-color: rgba(255, 247, 230, 1);
color: rgba(250, 140, 22, 1);
border-color: rgba(255, 213, 145, 1);
}
.dl4 {
background-color: rgba(249, 240, 255, 1);
color: rgba(114, 46, 209, 1);
border-color: rgba(211, 173, 247, 1);
}
.origin {
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 22px;
color: rgb(132, 136, 142);
cursor: pointer;
}
}
}
}
.line-test {
position: absolute;
top: 10px;
left: 109px;
height: 1300px;
border: 1px solid rgb(230, 231, 232);
z-index: -1;
}
}
.line {
width: 100%;
height: 1px;
background-color: rgb(234, 236, 238);
margin-top: 30px;
border: none;
.line-test {
position: absolute;
top: 10px;
left: 109px;
height: 1300px;
border: 1px solid rgb(230, 231, 232);
z-index: -1;
}
.pagination {
width: 100%;
height: 76px;
margin: 20px auto;
}
.pagination {
width: 100%;
height: 76px;
margin: 0 auto;
display: flex;
padding: 0 31px 0 36px;
justify-content: space-between;
align-items: center;
border-top: 1px solid rgb(234, 236, 238);
.total {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
}
:deep(.custom-pagination) {
display: flex;
padding: 0 31px 0 36px;
justify-content: space-between;
align-items: center;
border-top: 1px solid rgb(234, 236, 238);
.total {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
color: rgb(59, 65, 75);
}
:deep(.custom-pagination) {
display: flex;
align-items: center;
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li) {
min-width: 32px;
height: 32px;
line-height: 32px;
border-radius: 6px;
margin: 0 6px;
border: 1px solid rgba(0, 0, 0, 0.15);
background-color: #fff;
color: rgb(95, 101, 108);
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li.is-active) {
background-color: #fff;
color: rgba(22, 119, 255, 1);
border-color: rgba(22, 119, 255, 1);
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li.is-ellipsis) {
border: none;
background-color: transparent;
color: rgb(95, 101, 108);
min-width: 16px;
margin: 0 6px;
}
:deep(.custom-pagination.el-pagination.is-background .btn-prev),
:deep(.custom-pagination.el-pagination.is-background .btn-next) {
min-width: 32px;
height: 32px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.15);
background-color: #fff;
color: rgb(95, 101, 108);
font-size: 16px;
font-family: "Microsoft YaHei";
margin: 0 6px;
}
:deep(.custom-pagination.el-pagination.is-background .btn-prev.is-disabled),
:deep(.custom-pagination.el-pagination.is-background .btn-next.is-disabled) {
color: rgba(95, 101, 108, 0.45);
border-color: rgb(235, 238, 242);
background-color: #fff;
}
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li) {
min-width: 32px;
height: 32px;
line-height: 32px;
border-radius: 6px;
margin: 0 6px;
border: 1px solid rgba(0, 0, 0, 0.15);
background-color: #fff;
color: rgb(95, 101, 108);
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li.is-active) {
background-color: #fff;
color: rgba(22, 119, 255, 1);
border-color: rgba(22, 119, 255, 1);
}
:deep(.custom-pagination.el-pagination.is-background .el-pager li.is-ellipsis) {
border: none;
background-color: transparent;
color: rgb(95, 101, 108);
min-width: 16px;
margin: 0 6px;
}
:deep(.custom-pagination.el-pagination.is-background .btn-prev),
:deep(.custom-pagination.el-pagination.is-background .btn-next) {
min-width: 32px;
height: 32px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.15);
background-color: #fff;
color: rgb(95, 101, 108);
font-size: 16px;
font-family: "Microsoft YaHei";
margin: 0 6px;
}
:deep(.custom-pagination.el-pagination.is-background .btn-prev.is-disabled),
:deep(.custom-pagination.el-pagination.is-background .btn-next.is-disabled) {
color: rgba(95, 101, 108, 0.45);
border-color: rgb(235, 238, 242);
background-color: #fff;
}
}
}
.right {
width: 520px;
height: 100%;
.right-top {
width: 520px;
height: 625px;
background-color: rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
margin-bottom: 16px;
.title {
width: 100%;
height: 60px;
display: flex;
align-items: center;
padding: 14px 12px 20px 0;
.box {
width: 8px;
height: 20px;
background-color: rgb(5, 95, 194);
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
margin-right: 14px;
}
.text {
font-size: 20px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.btn {
width: 60px;
height: 28px;
margin-left: auto;
img {
width: 28px;
height: 28px;
cursor: pointer;
}
img:first-child {
margin-right: 4px;
}
.main-content {
width: 520px;
height: 540px;
padding: 0 48px 50px 34px;
.baseInfo {
width: 438px;
padding-bottom: 12px;
border-bottom: 1px solid rgb(234, 236, 238);
.baseInfo-item {
display: flex;
margin-bottom: 12px;
.baseInfo-item-title {
width: 88px;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
}
}
}
.main-content {
width: 520px;
height: 565px;
padding: 0 48px 50px 34px;
.baseInfo {
width: 438px;
height: 402px;
padding-bottom: 50px;
border-bottom: 1px solid rgb(234, 236, 238);
.baseInfo-item {
display: flex;
margin-bottom: 12px;
.baseInfo-item-title {
width: 88px;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
}
.address {
width: 110px;
}
.baseInfo-item-content {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
}
.long {
width: 349px;
}
.longlong {
width: 358px;
height: 88px;
display: flex;
flex-wrap: wrap;
align-content: space-between;
.span {
padding: 2px 8px;
border-radius: 4px;
background-color: rgba(230, 244, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
color: rgba(22, 119, 255, 1);
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
}
.span:nth-child(2n-1) {
margin-right: 2px;
}
}
.address {
width: 110px;
}
}
.company {
width: 438px;
height: 176px;
padding-top: 19px;
.company-title {
.baseInfo-item-content {
font-size: 16px;
font-weight: 700;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
margin-bottom: 19px;
}
.company-content {
width: 409px;
height: 114px;
.long {
width: 349px;
}
.longlong {
width: 358px;
display: flex;
flex-wrap: wrap;
overflow-y: scroll;
.company-item {
width: 180px;
height: 49px;
margin-bottom: 16px;
display: flex;
align-items: center;
cursor: pointer;
img {
width: 48px;
height: 48px;
margin-right: 8px;
}
.company-cn {
width: 130px;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.company-name {
width: 130px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.company-item:nth-child(2n-1) {
margin-right: 39px;
}
.company-item:last-child {
.company-name {
width: 150px;
}
gap: 4px;
.span {
padding: 2px 8px;
border-radius: 4px;
background-color: rgba(230, 244, 255, 1);
border: 1px solid rgba(186, 224, 255, 1);
color: rgba(22, 119, 255, 1);
font-size: 14px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 20px;
}
}
}
}
}
.right-bottom {
width: 520px;
height: 1292px;
background-color: rgba(255, 255, 255, 1);
border-radius: 10px;
box-shadow: 0px 0px 20px rgba(25, 69, 130, 0.1);
.title {
width: 100%;
height: 80px;
display: flex;
align-items: center;
padding: 14px 12px 40px 0;
.box {
width: 8px;
height: 20px;
background-color: rgb(5, 95, 194);
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
margin-right: 14px;
}
.text {
font-size: 20px;
.company {
width: 438px;
padding-top: 19px;
.company-title {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 26px;
color: rgb(5, 95, 194);
}
.btn {
width: 60px;
height: 28px;
margin-left: auto;
img {
width: 28px;
height: 28px;
cursor: pointer;
}
img:first-child {
margin-right: 4px;
}
line-height: 24px;
color: rgb(59, 65, 75);
margin-bottom: 19px;
}
}
.content-main {
width: 480px;
height: 1020px;
margin-left: 16px;
.content-item {
width: 454px;
margin-bottom: 60px;
margin-left: 26px;
position: relative;
.image01 {
width: 14px;
height: 12.13px;
position: absolute;
top: 8px;
left: -26px;
}
.content-item-time {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 30px;
color: rgb(5, 95, 194);
margin-bottom: 8px;
}
.content-item-title {
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 30px;
color: rgb(59, 65, 75);
margin-bottom: 8px;
}
.content-item-content {
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(95, 101, 108);
margin-bottom: 8px;
}
.content-item-door {
width: 300px;
height: 32px;
.company-content {
width: 409px;
max-height: 114px;
display: flex;
flex-wrap: wrap;
overflow-y: auto;
.company-item {
width: 180px;
height: 49px;
margin-bottom: 16px;
display: flex;
align-items: center;
padding: 4px 0 4px 11px;
border-radius: 4px;
background-color: rgba(255, 246, 240, 1);
border: 1px solid rgba(250, 140, 22, 0.4);
cursor: pointer;
img {
width: 20px;
height: 24px;
margin-right: 10px;
width: 48px;
height: 48px;
margin-right: 8px;
}
span {
.company-cn {
width: 130px;
font-size: 16px;
font-weight: 700;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgb(59, 65, 75);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.company-name {
width: 130px;
font-size: 16px;
font-weight: 400;
font-family: "Microsoft YaHei";
line-height: 24px;
color: rgba(255, 149, 77, 1);
color: rgb(95, 101, 108);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.company-item:nth-child(2n-1) {
margin-right: 39px;
}
}
}
}
......@@ -1552,20 +943,24 @@ const dialogData = ref([
height: 669px;
padding: 0;
border-radius: 4px;
.el-dialog__body {
padding: 0;
}
.el-dialog__header {
padding: 0;
margin: 0;
position: relative;
height: 48px;
}
.el-dialog__headerbtn {
top: 50%;
transform: translateY(-50%);
right: 12px;
}
.viewpoint-header {
width: 761px;
height: 48px;
......@@ -1575,6 +970,7 @@ const dialogData = ref([
padding: 0 24px;
border-bottom: 1px solid rgb(234, 236, 238);
}
.viewpoint-title {
font-size: 16px;
font-weight: 700;
......@@ -1582,11 +978,13 @@ const dialogData = ref([
line-height: 24px;
color: rgb(5, 95, 194);
}
.viewpoint-body {
padding: 24px 24px 35px 24px;
height: calc(669px - 48px);
box-sizing: border-box;
overflow: hidden;
.viewpoint-body-title {
font-size: 28px;
font-weight: 700;
......@@ -1595,6 +993,7 @@ const dialogData = ref([
color: rgb(59, 65, 75);
margin-bottom: 32px;
}
.viewpoint-item {
width: 713px;
min-height: 81px;
......@@ -1602,11 +1001,13 @@ const dialogData = ref([
margin-bottom: 12px;
position: relative;
padding-left: 48px;
img {
position: absolute;
top: -20px;
left: -20px;
}
.viewpoint-item-content {
width: 665px;
min-height: 81px;
......@@ -1619,6 +1020,7 @@ const dialogData = ref([
padding-top: 12px;
padding-bottom: 13px;
box-sizing: border-box;
.viewpoint-item-name {
font-size: 16px;
font-weight: 700;
......@@ -1627,6 +1029,7 @@ const dialogData = ref([
color: rgb(59, 65, 75);
margin-bottom: 5px;
}
.viewpoint-item-desc {
font-size: 16px;
font-weight: 400;
......@@ -1634,6 +1037,7 @@ const dialogData = ref([
line-height: 24px;
color: rgb(59, 65, 75);
}
.viewpoint-item-job {
position: absolute;
top: 8px;
......@@ -1648,4 +1052,157 @@ const dialogData = ref([
}
}
}
</style>
.tab-select {
width: 120px;
}
.viewpoint-dialog {
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
}
.viewpoint-dialog.el-dialog {
width: 761px !important;
height: 669px !important;
max-height: 669px !important;
border-radius: 10px;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1);
overflow: hidden;
display: flex;
flex-direction: column;
}
.viewpoint-dialog .el-dialog__header {
padding: 0;
margin: 0;
height: 52px;
flex-shrink: 0;
}
.viewpoint-dialog .el-dialog__body {
padding: 0;
flex: 1;
overflow: hidden;
}
.viewpoint-header {
display: flex;
justify-content: space-between;
align-items: center;
height: 52px;
padding: 0 24px;
border-bottom: 1px solid rgba(231, 243, 255, 1);
box-sizing: border-box;
}
.viewpoint-title {
display: flex;
align-items: center;
}
.viewpoint-tag {
font-size: 18px;
font-family: 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
font-weight: 700;
color: rgba(206, 79, 81, 1);
line-height: 24px;
}
.viewpoint-label {
font-size: 18px;
font-family: 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
font-weight: 700;
color: rgba(59, 65, 75, 1);
line-height: 24px;
}
.viewpoint-close {
width: 32px;
height: 32px;
cursor: pointer;
}
.viewpoint-body {
padding: 18px 24px 24px;
height: calc(669px - 52px);
max-height: calc(669px - 52px);
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
box-sizing: border-box;
}
.viewpoint-item {
display: flex;
gap: 8px;
align-items: flex-start;
}
.viewpoint-avatar {
flex-shrink: 0;
padding-top: 12px;
}
.viewpoint-content {
flex: 1;
display: flex;
align-items: flex-start;
}
.viewpoint-arrow {
width: 9px;
height: 72px;
flex-shrink: 0;
background: url('./assets/arrow-icon.png') no-repeat center / 100%;
}
.viewpoint-card {
flex: 1;
display: flex;
flex-direction: column;
gap: 2px;
padding: 8px 12px;
background-color: rgba(246, 250, 255, 1);
border: 1px solid rgba(231, 243, 255, 1);
border-radius: 4px;
}
.viewpoint-card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.viewpoint-name {
font-size: 16px;
font-family: 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
font-weight: 700;
color: rgba(59, 65, 75, 1);
line-height: 24px;
}
.viewpoint-job {
font-size: 16px;
font-family: 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
font-weight: 400;
color: rgba(95, 101, 108, 1);
line-height: 30px;
}
.viewpoint-desc {
font-size: 16px;
font-family: 'Source Han Sans CN', 'Microsoft YaHei', sans-serif;
font-weight: 400;
color: rgba(59, 65, 75, 1);
line-height: 24px;
text-align: justify;
}
.viewpoint-empty {
text-align: center;
padding: 40px 0;
color: rgba(170, 173, 177, 1);
font-size: 14px;
}
</style>
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论