提交 9c89e506 authored 作者: coderBryanFu's avatar coderBryanFu

fix:删除部分无用通用组件,全局样式优化

上级 e14bae6c
<template>
<div
class="card-title-custom"
:style="{ '--left-offset': leftOffset }"
>
{{ props.title }}
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
required: true
},
leftOffset: {
type: String,
default: '-22px'
}
})
</script>
<style scoped>
.card-title-custom {
font-size: 16px;
font-weight: 600;
color: #333;
position: relative;
}
.card-title-custom::before {
content: '';
position: absolute;
left: var(--left-offset, -22px);
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 16px;
background-color: #1459BB;
}
</style>
\ No newline at end of file
<template>
<div class="hello-world">
<el-card>
<template #header>
<div class="card-header">
<el-icon><Star /></el-icon>
<span>Hello World 组件</span>
</div>
</template>
<div class="content">
<h3>{{ title }}</h3>
<p>{{ description }}</p>
<el-divider />
<div class="counter-section">
<h4>计数器示例</h4>
<div class="counter">
<el-button type="primary" :icon="Minus" @click="decrement" :disabled="count <= 0"></el-button>
<el-input-number v-model="count" :min="0" :max="100" style="margin: 0 10px;"></el-input-number>
<el-button type="primary" :icon="Plus" @click="increment" :disabled="count >= 100"></el-button>
</div>
<el-progress :percentage="count" :color="progressColor" style="margin-top: 20px;"></el-progress>
</div>
<el-divider />
<div class="theme-section">
<h4>主题切换</h4>
<el-radio-group v-model="theme" @change="handleThemeChange">
<el-radio-button label="light">浅色主题</el-radio-button>
<el-radio-button label="dark">深色主题</el-radio-button>
</el-radio-group>
</div>
</div>
</el-card>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { Star, Plus, Minus } from '@element-plus/icons-vue'
// Props
const props = defineProps({
title: {
type: String,
default: '欢迎使用 Vue 3 + Element Plus'
},
description: {
type: String,
default: '这是一个可复用的组件示例,展示了 Vue 3 Composition API 的强大功能。'
}
})
// 响应式数据
const count = ref(0)
const theme = ref('light')
// 计算属性
const progressColor = computed(() => {
if (count.value < 30) return '#f56c6c'
if (count.value < 70) return '#e6a23c'
return '#67c23a'
})
// 方法
const increment = () => {
if (count.value < 100) {
count.value++
if (count.value === 100) {
ElMessage.success('恭喜!达到最大值!')
}
}
}
const decrement = () => {
if (count.value > 0) {
count.value--
if (count.value === 0) {
ElMessage.info('已归零')
}
}
}
const handleThemeChange = (value) => {
ElMessage.success(`已切换到${value === 'light' ? '浅色' : '深色'}主题`)
}
// 暴露给父组件的方法
defineExpose({
reset: () => {
count.value = 0
theme.value = 'light'
ElMessage.success('组件已重置')
}
})
</script>
<style scoped>
.hello-world {
margin: 20px 0;
}
.card-header {
display: flex;
align-items: center;
gap: 8px;
font-weight: bold;
}
.content h3 {
color: #409eff;
margin-bottom: 12px;
}
.content p {
color: #606266;
line-height: 1.6;
}
.counter-section,
.theme-section {
text-align: center;
}
.counter-section h4,
.theme-section h4 {
color: #303133;
margin-bottom: 16px;
}
.counter {
display: flex;
align-items: center;
justify-content: center;
}
</style>
\ No newline at end of file
<template>
<div class="box3">
<div class="box3-header">
<div class="box3-header-left">
<div class="box3-header-icon">
<img src="./image1.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 newsList" :key="index" @click="handleToNewsAnalysis(news)">
<div class="left">
<img :src="news[props.img] ? news[props.img] : DefaultIconNews" alt="" />
</div>
<div class="right">
<div class="right-top">
<div class="title"><span class="text-inner">{{ news[props.title] }}</span></div>
<div class="time">{{ news[props.from] }}</div>
</div>
<div class="right-footer">{{ news[props.content] }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import DefaultIconNews from "@/assets/icons/default-icon-news.png";
const props = defineProps({
// 新闻列表数据
newsList: {
type: Array,
default: () => []
},
img: {
type: String,
default: 'img'
},
title: {
type: String,
default: "title"
},
from: {
type: String,
default: "from"
},
content: {
type: String,
default: "content"
},
});
const emit = defineEmits(['item-click', 'more-click']);
const handleToMoreNews = () => {
emit('more-click')
};
const handleToNewsAnalysis = (item, index) => {
emit('item-click', item, index)
};
</script>
<style lang="scss" scoped>
.box3 {
width: 792px !important;
height: 450px !important;
border-radius: 10px !important;
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.1) !important;
background: rgba(255, 255, 255, 1) !important;
display: flex !important;
flex-direction: column;
gap: 0 !important;
overflow: hidden;
.box3-header {
height: 48px !important;
border-bottom: 1px solid rgba(234, 236, 238, 1) !important;
margin: 0 !important;
display: flex !important;
justify-content: space-between !important;
position: relative !important;
width: 100%;
box-sizing: border-box;
.box3-header-left {
display: flex !important;
.box3-header-icon {
margin-left: 19px !important;
margin-top: 14px !important;
width: 24px !important;
height: 24px !important;
img {
width: 100% !important;
height: 100% !important;
}
}
.box3-header-title {
margin-top: 11px !important;
margin-left: 17px !important;
height: 26px !important;
color: var(--color-main-active) !important;
font-family: 'Source Han Sans CN' !important;
font-size: 20px !important;
font-weight: 700 !important;
line-height: 26px !important;
}
}
.more {
width: 45px;
height: 24px;
position: absolute;
top: 12px;
right: 27px;
color: rgba(20, 89, 187, 1);
font-family: 'Source Han Sans CN';
font-size: 16px;
font-weight: 400;
line-height: 24px;
cursor: pointer;
}
}
.box3-main {
height: 401px;
overflow-y: auto;
overflow-x: hidden;
padding: 6px 0;
.box3-item {
display: flex;
height: 78px;
width: 749px;
margin-left: 21px;
border-bottom: 1px solid rgba(240, 242, 244, 1);
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: 72px;
height: 48px;
margin-top: 15px;
img {
width: 100%;
height: 100%;
}
}
.right {
width: 657px;
margin-left: 20px;
.right-top {
width: 657px;
display: flex;
justify-content: space-between;
.title {
margin-top: 14px;
width: 500px;
height: 24px;
color: rgba(59, 65, 75, 1);
font-family: 'Source Han Sans CN';
font-size: 16px;
font-weight: 700;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.text-inner {
border-bottom: 1px solid transparent;
}
}
.time {
width: 157px;
text-align: right;
height: 22px;
margin-top: 14px;
color: rgba(95, 101, 108, 1);
font-family: 'Source Han Sans CN';
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: 'Source Han Sans CN';
font-size: 16px;
font-weight: 400;
line-height: 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<div class="policy-list">
<div
v-for="(item, index) in policyList"
:key="index"
class="policy-item"
>
<div class="item-left">
<div class="report-cover">
<img :src="$withFallbackImage(item.imageUrl, index)" alt="Report Cover" />
</div>
</div>
<div class="item-right">
<h3 class="item-title">
{{ item.content }}
</h3>
<div class="item-meta">
<span class="meta-date">{{ formatDate(item.times) }}</span>
<span class="meta-divider">·</span>
<span class="meta-source">
{{ item.name }}
<el-icon class="link-icon"><TopRight /></el-icon>
</span>
</div>
<div class="item-tags" v-if="item.tags && item.tags.length">
<span v-for="(tag, tIndex) in item.tags" :key="tIndex" class="tag-pill">
{{ tag }}
</span>
</div>
<div class="item-actions" v-if="item.statusRaw">
<div
v-for="(statusItem, sIndex) in parseStatus(item.statusRaw)"
:key="sIndex"
class="status-link"
>
<span class="status-type">{{ statusItem.type }}</span>
<span class="status-year">{{ statusItem.year }}</span>
<span class="status-name">{{ statusItem.name }}</span>
<el-icon class="arrow-icon"><Right /></el-icon>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { TopRight, Right } from '@element-plus/icons-vue'
interface PolicyItem {
content: string;
statusRaw: string; // 原始的长字符串
name: string;
times: string;
tags?: string[];
coverUrl?: string;
}
const props = defineProps<{
policyList: PolicyItem[]
}>()
// 格式化日期:2025-06-26 -> 2025年6月26日
const formatDate = (dateStr: string) => {
if (!dateStr) return '';
const date = new Date(dateStr);
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}日`;
}
// 解析状态字符串
// 输入: "法案 2024 《芯片科学法案》; 政令 2025 《推动美国...》"
// 输出: 数组对象
const parseStatus = (raw: string) => {
if (!raw) return [];
// 按分号分割多个条目
const items = raw.split(/[;;]/).map(s => s.trim()).filter(s => s);
return items.map(itemStr => {
// 简单正则匹配: "类型 年份 《名称》"
// 注意:这里假设数据格式比较规范,实际需根据后端数据调整
// 尝试移除书名号进行提取
const cleanStr = itemStr.replace(/[《》]/g, '');
const parts = cleanStr.split(' ');
return {
type: parts[0] || '政策',
year: parts[1] || '',
name: parts.slice(2).join(' ') || cleanStr // 剩余部分作为名称
}
});
}
</script>
<style scoped>
.policy-list {
display: flex;
flex-direction: column;
}
.policy-item {
display: flex;
padding: 20px 0;
border-bottom: 1px solid #ebeef5;
gap: 16px;
transition: background-color 0.2s;
}
.policy-item:last-child {
border-bottom: none;
}
/* 左侧封面 */
.item-left {
flex-shrink: 0;
}
.report-cover {
width: 60px;
height: 80px;
background-color: #f2f3f5;
border: 1px solid #e4e7ed;
border-radius: 2px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.report-cover img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* 右侧内容 */
.item-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
/* 1. 标题 */
.item-title {
margin: 0 0 6px 0;
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
line-height: 1.4;
cursor: pointer;
}
.item-title:hover {
color: #409EFF;
}
/* 2. 元数据 */
.item-meta {
display: flex;
align-items: center;
font-size: 13px;
color: #606266;
margin-bottom: 8px;
}
.meta-divider {
margin: 0 8px;
font-weight: bold;
}
.meta-source {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
}
.meta-source:hover {
color: #409EFF;
}
.link-icon {
font-size: 12px;
}
/* 3. 标签 */
.item-tags {
display: flex;
gap: 8px;
margin-bottom: 10px;
}
.tag-pill {
background-color: #f2f3f5;
color: #5e6d82;
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
}
/* 4. 底部状态链接 */
.item-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.status-link {
display: inline-flex;
align-items: center;
background-color: #ecf5ff; /* 浅蓝色背景 */
color: #409EFF; /* 蓝色文字 */
padding: 4px 12px;
border-radius: 4px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.status-link:hover {
background-color: #d9ecff;
}
.status-type {
font-weight: bold;
margin-right: 4px;
}
.status-year {
margin-right: 4px;
}
.status-name {
margin-right: 4px;
}
.arrow-icon {
margin-left: 4px;
font-size: 12px;
}
</style>
\ No newline at end of file
<template>
<div class="policy-tracker-container">
<PolicyOverview />
<!-- 顶部搜索栏 -->
<div class="top-search-bar" v-if="props.showSearch">
<div class="search-left">
<el-input
v-model="searchQuery"
placeholder="搜索政策建议"
class="search-input"
size="default"
>
<template #suffix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div class="search-right">
<el-select v-model="dateRange" class="select-item" size="default">
<el-option label="近一年发布" value="last_year" />
<el-option label="近三年发布" value="last_three_years" />
</el-select>
<el-select v-model="sortOrder" class="select-item" size="default">
<el-option label="发布时间" value="time" />
<el-option label="热度" value="popularity" />
</el-select>
</div>
</div>
<el-row :gutter="24">
<el-col :span="6">
<aside class="filter-sidebar">
<div class="filter-group">
<CardTitle title="实施状态" style="margin-bottom: 10px"/>
<div class="checkbox-group">
<el-checkbox-group v-model="activeStatus">
<el-checkbox
v-for="status in statusFilters"
:key="status.id"
:label="status.id"
class="filter-checkbox"
>
<span
class="status-dot"
:style="`background-color: ${status.color}; border-color: ${status.borderColor || status.color || '#dcdfe6'}`"
></span>
{{ status.label }} ({{ status.count }})
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<div class="filter-group">
<CardTitle title="科技领域" style="margin-bottom: 10px"/>
<div class="checkbox-group">
<el-checkbox-group v-model="activeTechField">
<el-checkbox
v-for="field in techFieldFilters"
:key="field.id"
:label="field.id"
class="filter-checkbox tech-checkbox"
>
{{ field.label }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
</aside>
</el-col>
<el-col :span="18">
<div class="main-content">
<PolicyList :policyList="policies" />
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { Search } from '@element-plus/icons-vue';
import PolicyList from './PolicyList.vue';
import CardTitle from './CardTitle.vue';
import { getOverviewPolicy } from '@/api'
import PolicyOverview from '@/views/thinkTank/components/PolicyOverview.vue'
import { mockPolicyList } from '@/views/thinkTank/mockData';
const props = defineProps({
showSearch: {
type: Boolean,
default: true
}
})
// --- Sidebar Filters State & Data ---
const activeStatus = ref(['implemented']); // 改为数组支持多选
const activeTechField = ref(['ai']); // 改为数组支持多选
const statusFilters = ref([
{ id: 'implemented', label: '已实施', count: 18, color: '#e66657' },
{ id: 'partial', label: '部分实施', count: 12, color: '#e6a23c' },
{ id: 'unimplemented', label: '未实施', count: 12, color: '#909399' },
{ id: 'unknown', label: '未知状态', count: 4, color: 'transparent', borderColor: '#909399' },
]);
const techFieldFilters = ref([
{ id: 'ai', label: '人工智能' },
{ id: 'semiconductor', label: '半导体/芯片' },
{ id: 'energy', label: '能源与气候' },
{ id: 'international', label: '国际关系' },
{ id: 'economy', label: '经济决策' },
{ id: 'national_security', label: '国防与安全' },
]);
// --- Main Content State ---
const searchQuery = ref('');
const dateRange = ref('last_year');
const sortOrder = ref('time');
const policies = ref([]);
const getPolicies = async () => {
const { data } = await getOverviewPolicy({
researchTypeIds: activeTechField.value,
statusList: activeStatus.value,
})
// policies.value = data
policies.value = mockPolicyList
}
onMounted(() => {
getPolicies()
})
</script>
<style scoped>
.policy-tracker-container {
padding-top: 0;
}
/* 顶部搜索栏样式 */
.top-search-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.search-left {
flex: 1;
max-width: 400px;
}
.search-input {
width: 100%;
}
.search-input :deep(.el-input__wrapper) {
border-radius: 6px;
box-shadow: 0 0 0 1px #dcdfe6;
transition: box-shadow 0.3s ease;
}
.search-input :deep(.el-input__wrapper:hover) {
box-shadow: 0 0 0 1px #409EFF;
}
.search-input :deep(.el-input__wrapper.is-focus) {
box-shadow: 0 0 0 1px #409EFF;
}
.search-right {
display: flex;
align-items: center;
gap: 12px;
}
.select-item {
width: 140px;
}
.select-item :deep(.el-input__wrapper) {
border-radius: 6px;
box-shadow: 0 0 0 1px #dcdfe6;
transition: box-shadow 0.3s ease;
}
.select-item :deep(.el-input__wrapper:hover) {
box-shadow: 0 0 0 1px #409EFF;
}
.main-content {
background-color: #fff;
padding: 8px;
}
/* --- Sidebar Styles --- */
.filter-sidebar {
background-color: #fff;
padding: 20px;
border-radius: 4px;
}
.filter-group {
margin-bottom: 25px;
}
.filter-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin: 0 0 15px 0;
display: flex;
align-items: center;
}
.implementation-title {
padding-left: 12px;
position: relative;
}
.implementation-title::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 16px;
background-color: #409EFF;
border-radius: 2px;
}
.icon-square {
display: inline-block;
width: 4px;
height: 16px;
background-color: #409EFF;
margin-right: 8px;
border-radius: 2px;
}
.filter-sidebar ul {
list-style: none;
padding: 0;
margin: 0;
}
/* 复选框组样式 */
.checkbox-group {
display: flex;
flex-direction: column;
gap: 8px;
}
.filter-checkbox {
margin: 0 !important;
padding: 8px 12px;
border-radius: 4px;
transition: all 0.3s ease;
width: 100%;
display: flex;
align-items: center;
}
.filter-checkbox:hover {
background-color: #f0f2f5;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 0;
border: 1px solid transparent;
display: inline-block;
flex-shrink: 0;
}
/* 响应式设计 */
@media (max-width: 768px) {
.top-search-bar {
flex-direction: column;
gap: 16px;
padding: 16px;
}
.search-left {
max-width: none;
width: 100%;
}
.search-right {
width: 100%;
justify-content: center;
gap: 8px;
}
.select-item {
width: 120px;
}
}
@media (max-width: 480px) {
.search-right {
flex-direction: column;
gap: 8px;
}
.select-item {
width: 100%;
max-width: 200px;
}
}
</style>
\ No newline at end of file
......@@ -55,6 +55,7 @@ import { ref, computed, onMounted, watchEffect } from "vue";
import { useRouter } from "vue-router";
import { useRoute } from "vue-router";
import { getPersonType } from "@/api/common/index";
import SearchBar from "@/components/layout/SearchBar.vue";
import Menu1 from "@/assets/icons/overview/menu1.png";
import Menu2 from "@/assets/icons/overview/menu2.png";
......
<template>
<div class="header-wrapper">
<div class="icon"></div>
<div class="title"></div>
<div class="btn-box"></div>
</div>
</template>
\ No newline at end of file
<template>
<div class="info-wrapper">
<!-- <div class="header-item">国家科技安全</div>
<div class="header-item">></div> -->
<div class="header-item back-item" @click="handleBackHome">中美博弈概览</div>
<div class="header-item">></div>
<div class="header-item">{{ curTitleName }}</div>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import router from "@/router";
const props = defineProps({
curTitleName: {
type: String,
required: true
}
});
// 返回首页
const handleBackHome = () => {
router.push({
path: "/overview"
});
};
</script>
<style lang="scss" scoped>
.info-wrapper {
height: 64px;
line-height: 64px;
display: flex;
justify-content: flex-end;
font-family: Microsoft YaHei;
font-size: 20px;
font-weight: 700;
color: #fff;
.header-item {
margin: 0 3px;
}
.back-item {
cursor: pointer;
&:hover {
color: #ccc;
}
}
}
</style>
\ No newline at end of file
<template>
<div class="menu-wrapper">
<div class="menu-item" @click="handleToOverview">
<div class="menu-item-icon">
<img src="@/assets/icons/home-header-icon1.png" alt="" />
</div>
<div class="menu-item-text">{{ "首页" }}</div>
</div>
<div
class="menu-item"
@click="handleToGjOverview"
@mouseenter="handleIsShowCountryMore(true)"
@mouseleave="handleIsShowCountryMore(false)"
>
<div class="menu-item-icon1">
<img src="@/assets/icons/home-header-icon2.png" alt="" />
</div>
<div class="menu-item-text">{{ "国家" }}</div>
</div>
<div class="menu-item">
<div class="menu-item-icon2">
<img src="@/assets/icons/home-header-icon3.png" alt="" />
</div>
<div class="menu-item-text">{{ "领域" }}</div>
</div>
<div class="menu-item">
<div class="menu-item-icon3">
<img src="@/assets/icons/home-header-icon4.png" alt="" />
</div>
<div class="menu-item-text">{{ "要素" }}</div>
</div>
<div class="menu-item">
<div class="menu-item-icon4">
<img src="@/assets/icons/home-header-icon5.png" alt="" />
</div>
<div class="menu-item-text">{{ "事件" }}</div>
</div>
</div>
<div
class="more-wrapper"
v-if="isShowCountryMore"
@mouseenter="handleIsShowCountryMore(true)"
@mouseleave="handleIsShowCountryMore(false)"
>
<div class="left">
<div class="left-header">
<div class="title">{{ "中美科技博弈概览" }}</div>
<div class="icon">
<img src="@/assets/icons/more.png" alt="" />
</div>
</div>
<div class="left-main">
<div class="item" v-for="(item, index) in leftList" :key="index" @click="handleClickItem(item)">
<div class="icon"></div>
<div class="text">{{ item.name }}</div>
</div>
</div>
</div>
<!-- <div class="right">
<div class="right-header">
<div class="title">{{ "风险监测" }}</div>
<div class="icon">
<img src="@/assets/icons/more.png" alt="" />
</div>
</div>
<div class="right-main">
<div class="item" v-for="(item, index) in rightList" :key="index">
<div class="icon"></div>
<div class="text">{{ item.name }}</div>
</div>
</div>
</div> -->
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import router from "@/router";
const isShowCountryMore = ref(false);
const handleIsShowCountryMore = isShow => {
isShowCountryMore.value = isShow;
};
const handleToOverview = () => {
router.push({
path: "/overview"
});
};
const handleToGjOverview = () => {
router.push({
path: "/gjOverview"
});
};
const leftList = ref([
{
name: "科技法案",
path: "/billHome"
},
{
name: "科技政令",
path: "/decree"
},
{
name: "美国科技智库",
path: "/thinkTank"
},
{
name: "出口管制",
path: "/exportControl"
},
{
name: "科研合作限制",
path: "/cooperationRestrictions"
},
{
name: "投融资限制",
path: "/finance"
},
{
name: "市场准入限制",
path: "/marketAccessRestrictions"
},
{
name: "规则限制",
path: "/ruleRestrictions"
},
{
name: "美国科技人物观点",
path: "/technologyFigures"
},
{
name: "美国主要创新主体动向",
path: "/innovationSubject"
},
{
name: "美国科研资助体系分析",
path: "/scientificFunding"
}
]);
const handleClickItem = item => {
const curRoute = router.resolve({
path: item.path
});
window.open(curRoute.href, "_blank");
};
const rightList = ref([
{
name: "科技战略布局",
path: ""
},
{
name: "创新体系位势分析",
path: ""
}
]);
</script>
<style lang="scss" scoped>
.menu-wrapper {
width: 644px;
height: 64px;
display: flex;
justify-content: space-between;
.menu-item {
display: flex;
gap: 11px;
width: 112px;
height: 64px;
justify-content: center;
align-items: center;
cursor: pointer;
&:hover {
background: var(--color-main-active);
}
.menu-item-icon {
// margin-top: 2px;
width: 22px;
height: 22px;
img {
width: 100%;
height: 100%;
}
}
.menu-item-icon1 {
// margin-top: 4px;
width: 24px;
height: 24px;
img {
width: 100%;
height: 100%;
}
}
.menu-item-icon2 {
// margin-top: 4px;
width: 24px;
height: 22px;
img {
width: 100%;
height: 100%;
}
}
.menu-item-icon3 {
// margin-top: 2px;
width: 20px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.menu-item-icon4 {
width: 22px;
height: 20px;
img {
width: 100%;
height: 100%;
}
}
.menu-item-text {
// margin-top: 16px;
height: 32px;
color: rgba(255, 255, 255, 1);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 24px;
font-weight: 700;
line-height: 32px;
letter-spacing: 0px;
text-align: left;
}
}
}
.more-wrapper {
position: absolute;
z-index: 99999;
top: 64px;
left: 0;
width: 100%;
height: 299px;
background: rgb(249, 249, 249);
box-shadow: 0px 0px 20px 0px rgba(25, 69, 130, 0.3);
display: flex;
.left {
margin-top: 35px;
margin-left: 179px;
width: 769px;
height: 218px;
.left-header {
display: flex;
height: 26px;
align-items: center;
.title {
width: 160px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.icon {
margin-top: -8px;
margin-left: 8px;
width: 10px;
height: 10px;
img {
width: 100%;
height: 100%;
}
}
}
.left-main {
height: 192px;
display: flex;
flex-wrap: wrap;
.item {
margin-top: 18px;
display: flex;
width: 256px;
height: 36px;
align-items: center;
border-radius: 4px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
.icon {
background: var(--color-main-active) !important;
}
.text {
color: var(--color-main-active) !important;
font-weight: 700;
font-size: 20px;
}
}
.icon {
width: 6px;
height: 6px;
border-radius: 3px;
background: rgba(95, 101, 108, 1);
}
.text {
margin-left: 10px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
.right {
margin-top: 35px;
margin-left: 53px;
width: 192px;
height: 116px;
.right-header {
display: flex;
height: 26px;
align-items: center;
.title {
width: 80px;
height: 26px;
color: var(--color-main-active);
font-family: Microsoft YaHei;
font-style: Bold;
font-size: 20px;
font-weight: 700;
line-height: 26px;
letter-spacing: 0px;
text-align: left;
}
.icon {
margin-top: -8px;
margin-left: 8px;
width: 10px;
height: 10px;
img {
width: 100%;
height: 100%;
}
}
}
.right-main {
.item {
margin-top: 18px;
display: flex;
width: 256px;
height: 36px;
align-items: center;
border-radius: 4px;
cursor: pointer;
&:hover {
background: var(--color-bg-hover);
.icon {
background: var(--color-main-active) !important;
}
.text {
color: var(--color-main-active) !important;
font-weight: 700;
font-size: 20px;
}
}
.icon {
width: 6px;
height: 6px;
border-radius: 3px;
background: rgba(95, 101, 108, 1);
}
.text {
margin-left: 10px;
height: 24px;
color: rgba(95, 101, 108, 1);
font-family: Microsoft YaHei;
font-style: Regular;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0px;
text-align: left;
}
}
}
}
}
</style>
\ No newline at end of file
......@@ -4,7 +4,6 @@ import router from "./router";
import ElementPlus from "element-plus";
import "element-plus/dist/index.css";
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
import CardTitle from "./components/CardTitle.vue";
import { withFallbackImage } from "./utils";
import "./styles/scrollbar.css";
import "./styles/elui.css";
......@@ -12,18 +11,18 @@ import "./styles/main.css";
import '@/assets/fonts/font.css'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import AreaTag from '@/components/base/AreaTag/index.vue'
import LeftBtn from "@/components/base/PageBtn/LeftBtn.vue";
import RightBtn from "@/components/base/PageBtn/RightBtn.vue";
import OverviewMainBox from "@/components/base/BoxBackground/OverviewMainBox.vue";
import OverviewNormalBox from "@/components/base/BoxBackground/OverviewNormalBox.vue";
import AnalysisBox from '@/components/base/BoxBackground/AnalysisBox.vue'
import NewsList from '@/components/base/NewsList/index.vue'
import ModuleHeader from '@/components/base/ModuleHeader/index.vue'
import RiskSignal from "@/components/base/RiskSignal/index.vue";
import MessageBubble from "@/components/base/MessageBubble/index.vue";
import SourceTabLsit from '@/components/base/SourceTabList/index.vue'
import ActionButton from "@/components/base/ActionButton/index.vue"
// import AreaTag from '@/components/base/AreaTag/index.vue'
// import LeftBtn from "@/components/base/PageBtn/LeftBtn.vue";
// import RightBtn from "@/components/base/PageBtn/RightBtn.vue";
// import OverviewMainBox from "@/components/base/BoxBackground/OverviewMainBox.vue";
// import OverviewNormalBox from "@/components/base/BoxBackground/OverviewNormalBox.vue";
// import AnalysisBox from '@/components/base/BoxBackground/AnalysisBox.vue'
// import NewsList from '@/components/base/NewsList/index.vue'
// import ModuleHeader from '@/components/base/ModuleHeader/index.vue'
// import RiskSignal from "@/components/base/RiskSignal/index.vue";
// import MessageBubble from "@/components/base/MessageBubble/index.vue";
// import SourceTabLsit from '@/components/base/SourceTabList/index.vue'
// import ActionButton from "@/components/base/ActionButton/index.vue"
// 引入 Pinia 实例
import pinia from './stores'
......@@ -41,19 +40,18 @@ app.use(ElementPlus, {
locale: zhCn,
})
app.use(pinia) // 挂载 Pinia
app.component("CardTitle", CardTitle);
app.component('AreaTag', AreaTag) // 领域标签
app.component('LeftBtn', LeftBtn) // 向左按钮
app.component('RightBtn', RightBtn) // 向右按钮
app.component('OverviewMainBox', OverviewMainBox) // 概览页最新动态背景盒子
app.component('OverviewNormalBox', OverviewNormalBox) // 概览页一版模块背景盒子
app.component('AnalysisBox', AnalysisBox) // 分析页模块背景
app.component('ModuleHeader', ModuleHeader) // 模块头部
app.component('RiskSignal', RiskSignal) // 风险信号
app.component('NewsList', NewsList) // 新闻资讯
app.component('MessageBubble', MessageBubble) // 社交媒体
app.component('SourceTabLsit', SourceTabLsit) // 资源库tab列表
app.component('ActionButton', ActionButton) // 普通按钮和激活按钮
// app.component('AreaTag', AreaTag) // 领域标签
// app.component('LeftBtn', LeftBtn) // 向左按钮
// app.component('RightBtn', RightBtn) // 向右按钮
// app.component('OverviewMainBox', OverviewMainBox) // 概览页最新动态背景盒子
// app.component('OverviewNormalBox', OverviewNormalBox) // 概览页一版模块背景盒子
// app.component('AnalysisBox', AnalysisBox) // 分析页模块背景
// app.component('ModuleHeader', ModuleHeader) // 模块头部
// app.component('RiskSignal', RiskSignal) // 风险信号
// app.component('NewsList', NewsList) // 新闻资讯
// app.component('MessageBubble', MessageBubble) // 社交媒体
// app.component('SourceTabLsit', SourceTabLsit) // 资源库tab列表
// app.component('ActionButton', ActionButton) // 普通按钮和激活按钮
app.mount("#app");
......@@ -6,7 +6,7 @@
--color-bg-hover: #e7f3ff;
--color-bg-hover: #f6faff;
/* 普通按钮颜色 */
--btn-plain-border-color: rgba(230, 231, 232, 1);
......@@ -17,27 +17,8 @@
--btn-active-bg-color: rgba(231, 243, 255, 1);
--btn-active-text-color: var(--color-main-active);
/* 标签按钮颜色 */
--tag-btn1-bg-color: rgba(255, 241, 240, 1);
--tag-btn1-border-color: rgba(255, 204, 199, 1);
--tag-btn1-text-color: rgba(255, 77, 79, 1);
--tag-btn2-bg-color: rgba(255, 251, 230, 1);
--tag-btn2-border-color: rgba(255, 241, 184, 1);
--tag-btn2-text-color: rgba(250, 173, 20, 1);
--tag-btn2-bg-color: rgba(230, 244, 255, 1);
--tag-btn2-border-color: rgba(186, 224, 255, 1);
--tag-btn2-text-color: rgba(22, 119, 255, 1);
--tag-btn3-bg-color: rgba(246, 255, 237, 1);
--tag-btn3-border-color: rgba(217, 247, 190, 1);
--tag-btn3-text-color: rgba(82, 196, 26, 1);
/* 文字颜色 设计定义*/
--text-primary-color: #0a121e;
--text-primary-90-color: #222934;
--text-primary-80-color: #3b414b;
--text-primary-65-color: #5f656c;
......
......@@ -3,7 +3,7 @@
<div class="home-wrapper">
<div class="home-header">
<div class="header-left">
<HeaderMenu></HeaderMenu>
<!-- <HeaderMenu></HeaderMenu> -->
</div>
<div class="header-right">
<input :value="input">
......@@ -26,7 +26,7 @@
<script setup>
import { onMounted, ref, computed } from "vue";
import HeaderMenu from "@/components/headerMenu.vue";
// import HeaderMenu from "@/components/headerMenu.vue";
import RiskToday from './component/riskToday/index.vue'
import ScienceNews from './component/scienceNews/index.vue'
const input = ref('')
......
<!--ZM博弈概览页-->
<template>
<div class="home-wrapper">
<!-- <div class="home-header">
<div class="header-left">
<HeaderMenu></HeaderMenu>
</div>
<div class="header-right">
<div class="header-search-box">
<div class="header-search-left">
<div class="input-box">
<el-input v-model="searchText" @keyup.enter="handleSearch" />
</div>
<div class="icon" @click="handleSearch">
<img src="./assets/images/search-icon.png" alt="" />
</div>
</div>
<div class="header-search-right" @click="handleToSearch">
<div class="header-img-box"><img src="./assets/images/search-btn.png" alt="" /></div>
</div>
</div>
</div>
</div> -->
<div class="content-box">
<div class="home-top-bg"></div>
<!-- 导航栏 -->
......@@ -81,7 +60,6 @@ import { onMounted, onUnmounted, ref, computed, provide } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import background from "./assets/images/backgroundBT.png";
import HeaderMenu from "@/components/headerMenu.vue";
import right from "./assets/icons/right.png";
import commonTitle from "./commonTitle/comTitle.vue";
import week from "./assets/icons/week.png";
......
......@@ -841,9 +841,9 @@ onMounted(() => {
color: rgba(19, 168, 168, 1);
}
.tag2 {
background: var(--tag-btn1-bg-color);
border: 1px solid var(--tag-btn1-border-color);
color: var(--tag-btn1-text-color);
background: rgba(255, 241, 240, 1);
border: 1px solid rgba(255, 204, 199, 1);
color: rgba(255, 77, 79, 1);
}
}
}
......
......@@ -2758,8 +2758,8 @@ onMounted(async () => {
line-height: 24px;
border-radius: 4px;
padding: 0 8px;
background: var(--tag-btn2-bg-color);
color: var(--tag-btn2-text-color);
background: rgba(255, 251, 230, 1);
color: rgba(250, 173, 20, 1);
font-family: Microsoft YaHei;
font-size: 14px;
font-weight: 400;
......
......@@ -602,8 +602,6 @@ import NewsList from "@/components/NewsList/NewsList.vue";
import RiskSignal from "@/components/RiskSignal/RiskSignal.vue";
import { onMounted, ref, computed, reactive, shallowRef, watch, nextTick } from "vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
const homeMainRef = ref(null);
const { isShow } = useContainerScroll(homeMainRef);
import * as echarts from "echarts";
......
......@@ -463,8 +463,6 @@
import RiskSignal from "@/components/RiskSignal/RiskSignal.vue";
import { onMounted, ref, computed } from "vue";
import scrollToTop from "@/utils/scrollToTop";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import setChart from "@/utils/setChart";
import * as echarts from "echarts";
import { DArrowRight, Warning, Search } from "@element-plus/icons-vue";
......
......@@ -379,8 +379,7 @@ import NewsList from "@/components/NewsList/NewsList.vue";
import { onMounted, ref, computed } from "vue";
import * as echarts from "echarts";
import router from "@/router";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import DivideHeader from "@/components/DivideHeader.vue";
import scrollToTop from "@/utils/scrollToTop";
import { useContainerScroll } from "@/hooks/useScrollShow";
......
......@@ -537,8 +537,6 @@ import setChart from "@/utils/setChart";
import scrollToTop from "@/utils/scrollToTop";
import router from "@/router";
import DivideHeader from "@/components/DivideHeader.vue";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import { getPersonSummaryInfo } from "@/api/common/index";
import getMultiLineChart from "./utils/multiLineChart";
import getPieChart from "./utils/piechart";
......
......@@ -45,8 +45,6 @@ import newData from "./components/dataNew/index.vue";
import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
// import { useContainerScroll } from "@/hooks/useScrollShow";
import { getStatCount } from "@/api/ruleRestriction/index.js";
......
......@@ -144,8 +144,6 @@ import newData from "./components/dataNew/index.vue";
import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import { useContainerScroll } from "@/hooks/useScrollShow";
import { getStatCount } from '@/api/ruleRestriction/index.js'
......
......@@ -123,8 +123,6 @@ import newData from "./components/dataNew/index.vue";
import askPage from "./components/askPage/index.vue";
import dataSub from "./components/dataSub/index.vue";
import resLib from "./components/resLib/index.vue";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import scrollToTop from "@/utils/scrollToTop";
......
<template>
<el-row :gutter="24">
<el-col :span="6">
<aside class="filter-sidebar">
<div class="filter-group">
<CardTitle title="报告类型" style="margin-bottom: 10px"/>
<el-checkbox-group v-model="selectedReportTypes">
<el-checkbox label="研究报告" />
<el-checkbox label="简报" />
<el-checkbox label="会议记录" />
<el-checkbox label="期刊文章" />
</el-checkbox-group>
</div>
<div class="filter-group">
<CardTitle title="研究类型" style="margin-bottom: 10px"/>
<el-checkbox-group v-model="selectedResearchTypes">
<el-checkbox label="人工智能" />
<el-checkbox label="半导体/芯片" />
<el-checkbox label="能源与气候" />
<el-checkbox label="国际关系" />
<el-checkbox label="经济决策" />
<el-checkbox label="国防与安全" />
</el-checkbox-group>
</div>
<div class="filter-group">
<CardTitle title="作者" style="margin-bottom: 10px"/>
<el-input v-model="authorName" placeholder="输入作者名称" />
</div>
</aside>
</el-col>
<el-col :span="18">
<div class="report-content">
<div class="content-toolbar">
<el-input
v-model="searchQuery"
placeholder="按报告标题检索"
:prefix-icon="Search"
style="width: 240px;"
/>
<div class="sort-options">
<el-select v-model="sourceType" placeholder="来源类型" style="width: 120px; margin-right: 10px;">
<el-option label="全部" value="all"></el-option>
<el-option label="原创" value="original"></el-option>
</el-select>
<el-select v-model="publishDateSort" placeholder="发布时间" style="width: 120px;">
<el-option label="最新" value="desc"></el-option>
<el-option label="最早" value="asc"></el-option>
</el-select>
</div>
</div>
<el-row :gutter="20">
<el-col :span="8" v-for="report in reportList" :key="report.id" class="card-col">
<el-card shadow="hover" class="report-card" :body-style="{ padding: '0px' }">
<el-image :src="$withFallbackImage(report.imageUrl, report.id) " fit="cover" class="card-image" lazy></el-image>
<div class="card-content">
<h4 class="card-title">{{ report.name }}</h4>
<div class="card-meta">
<span class="card-date">{{ report.times }}</span>
<span class="card-source">{{ props.thinkTankName }}</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import { Search } from '@element-plus/icons-vue';
import { getThinkTankReport } from '@/api';
import { useRoute } from 'vue-router';
import { mockReportList } from '../mockData';
const route = useRoute();
const props = defineProps({
thinkTankName: {
type: String,
required: true
}
});
// Filters State
const selectedReportTypes = ref(['研究报告', '简报']);
const selectedResearchTypes = ref(['人工智能']);
const authorName = ref('');
// Toolbar State
const searchQuery = ref('');
const sourceType = ref('all');
const publishDateSort = ref('desc');
// Mock Data for Report Cards
const reportList = ref([
{ id: 1, title: '中国对AI的转型产业政策', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1611162617474-5b21e879e113?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
{ id: 2, title: '中美对抗、竞争和合作将跨越人工智能通用领域的五个...', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1574015974293-817f0e62f0f3?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
{ id: 3, title: '中国、智慧城市和中东:地区和美国的选择', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1593989931843-3f17374312b3?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
{ id: 4, title: '中国对AI的转型产业政策', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1695423635441-89d968a528f4?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
{ id: 5, title: '中美经济竞争:复杂经济和地缘政治关系中的收益...', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1616121213037-1439247c6178?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
{ id: 6, title: '中国、智慧城市和中东:留给地区和美国的选择', date: '2025年6月26日', source: '兰德科技智库', imageUrl: 'https://images.unsplash.com/photo-1542372147-7a2846215392?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&w=600' },
]);
onMounted(() => {
getThinkTankReport({ id: route.params.id }).then(res => {
// reportList.value = res.data;
reportList.value = mockReportList;
});
})
</script>
<style scoped>
.filter-sidebar .filter-group {
margin-bottom: 24px;
}
.filter-sidebar .filter-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin: 0 0 12px 0;
}
.filter-sidebar .el-checkbox-group {
display: flex;
flex-direction: column;
}
.filter-sidebar .el-checkbox {
margin-bottom: 10px;
}
.filter-sidebar .el-checkbox:last-child {
margin-bottom: 0;
}
/* Right Content Grid Styles */
.content-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.card-col {
margin-bottom: 20px;
}
.report-card {
cursor: pointer;
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
border: 1px solid #e4e7ed;
padding: 10px;
}
.report-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.card-image {
width: 100%;
height: 160px;
display: block;
}
.card-content {
padding: 16px;
}
.card-title {
font-size: 15px;
font-weight: 600;
color: #303133;
margin: 0 0 10px 0;
line-height: 1.4;
/* overflow: hidden; */
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
overflow: hidden;
-webkit-box-orient: vertical;
}
.card-meta {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #909399;
}
</style>
\ No newline at end of file
......@@ -333,7 +333,7 @@
<script setup>
import RiskSignal from "@/components/RiskSignal/RiskSignal.vue";
import NewsList from "@/components/NewsList/NewsList.vue";
import NewsList from "@/components/base/NewsList/index.vue";
import { onMounted, ref, computed, reactive, nextTick } from "vue";
import scrollToTop from "@/utils/scrollToTop";
import router from "@/router";
......@@ -354,8 +354,6 @@ import {
getThinkTankReportRemarks
} from "@/api/thinkTank/overview";
import { getPersonSummaryInfo } from "@/api/common/index";
import HeaderMenu from "@/components/headerMenu.vue";
import headerInfo from "@/components/headerInfo.vue";
import getMultiLineChart from "./utils/multiLineChart";
import getPieChart from "./utils/piechart";
import getSankeyChart from "./utils/sankey";
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论