准备覆盖远程仓库
This commit is contained in:
parent
7979e6ea4c
commit
bdf8cdd8c6
28
package.json
28
package.json
|
|
@ -8,17 +8,26 @@
|
|||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/d3": "^7.4.3",
|
||||
"core-js": "^3.8.3",
|
||||
"vue": "^3.2.13"
|
||||
"d3": "^7.9.0",
|
||||
"element-plus": "^2.13.0",
|
||||
"vue": "^3.2.13",
|
||||
"vue-router": "4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@types/node": "^25.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.52.0",
|
||||
"@typescript-eslint/parser": "^8.52.0",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-plugin-typescript": "^5.0.9",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3"
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
|
@ -30,9 +39,20 @@
|
|||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"ecmaVersion": 2020,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {}
|
||||
"rules": {},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.vue"],
|
||||
"parser": "vue-eslint-parser",
|
||||
"parserOptions": {
|
||||
"parser": "@typescript-eslint/parser"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
|
|
|||
|
|
@ -14,4 +14,10 @@
|
|||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
|
|
|
|||
29
src/App.vue
29
src/App.vue
|
|
@ -1,16 +1,12 @@
|
|||
<template>
|
||||
<img alt="Vue logo" src="./assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
name: 'App'
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -21,6 +17,21 @@ export default {
|
|||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
nav {
|
||||
padding: 30px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
nav a {
|
||||
font-weight: bold;
|
||||
color: #2c3e50;
|
||||
text-decoration: none;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
nav a.router-link-exact-active {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
<template>
|
||||
<footer class="page-footer">
|
||||
<div class="footer-container">
|
||||
<div class="footer-content">
|
||||
<div class="footer-section">
|
||||
<div class="footer-brand">
|
||||
<div class="brand-logo">
|
||||
<div class="brand-mark"></div>
|
||||
<h3>科技大脑智慧服务平台</h3>
|
||||
</div>
|
||||
<p class="brand-description">
|
||||
致力于整合优质科技资源,为企业和个人提供全方位的科技创新服务,
|
||||
推动科技成果转化,助力创新发展。
|
||||
</p>
|
||||
<div class="social-links">
|
||||
<el-button circle size="small" class="social-btn">
|
||||
<el-icon>
|
||||
<ChatDotRound />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button circle size="small" class="social-btn">
|
||||
<el-icon>
|
||||
<Message />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
<el-button circle size="small" class="social-btn">
|
||||
<el-icon>
|
||||
<Phone />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4>服务导航</h4>
|
||||
<ul class="footer-links">
|
||||
<li>
|
||||
<router-link to="/news-policy">新闻政策</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/smart-qa">智慧问答</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/tech-resources">科技资源</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link to="/talent-profile">人才画像</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4>政策法规</h4>
|
||||
<ul class="footer-links">
|
||||
<li>
|
||||
<a href="#">高新技术企业认定</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">科技成果转化政策</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">人才引进政策</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">创新创业扶持</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4>帮助中心</h4>
|
||||
<ul class="footer-links">
|
||||
<li>
|
||||
<a href="#">使用指南</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">常见问题</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">联系我们</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#">意见反馈</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="footer-section">
|
||||
<h4>联系信息</h4>
|
||||
<div class="contact-info">
|
||||
<div class="contact-item">
|
||||
<el-icon>
|
||||
<Location />
|
||||
</el-icon>
|
||||
<span>长春理工大学</span>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<el-icon>
|
||||
<Phone />
|
||||
</el-icon>
|
||||
<span>400-123-4567</span>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<el-icon>
|
||||
<Message />
|
||||
</el-icon>
|
||||
<span>service@techbrain.com</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="footer-bottom">
|
||||
<div class="copyright">
|
||||
<p>© 2024 科技大脑智慧服务平台. 保留所有权利.</p>
|
||||
</div>
|
||||
<div class="footer-links-bottom">
|
||||
<a href="#">隐私政策</a>
|
||||
<span class="divider">|</span>
|
||||
<a href="#">服务条款</a>
|
||||
<span class="divider">|</span>
|
||||
<a href="#">网站地图</a>
|
||||
<span class="divider">|</span>
|
||||
<a href="#">备案号: 京ICP备12345678号</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 页脚组件
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-footer {
|
||||
background: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 60px 40px 30px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr 1fr 1.5fr;
|
||||
gap: 40px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.footer-section h4 {
|
||||
color: #409eff;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.footer-brand .brand-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 20px;
|
||||
height: 5px;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(90deg, #409eff, #337ecc);
|
||||
box-shadow: 0 0 10px rgba(64, 158, 255, 0.6);
|
||||
}
|
||||
|
||||
.footer-brand h3 {
|
||||
color: #409eff;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.brand-description {
|
||||
color: #bdc3c7;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.social-links {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.social-btn {
|
||||
background: rgba(64, 158, 255, 0.1);
|
||||
border-color: rgba(64, 158, 255, 0.3);
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.social-btn:hover {
|
||||
background: rgba(64, 158, 255, 0.2);
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer-links li {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.footer-links a {
|
||||
color: #bdc3c7;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links a:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.contact-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #bdc3c7;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.contact-item .el-icon {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.copyright p {
|
||||
color: #95a5a6;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footer-links-bottom {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.footer-links-bottom a {
|
||||
color: #95a5a6;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
.footer-links-bottom a:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.footer-links-bottom .divider {
|
||||
color: #7f8c8d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.footer-section:first-child {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 30px;
|
||||
}
|
||||
|
||||
.footer-bottom {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-container {
|
||||
padding: 40px 20px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
<template>
|
||||
<header class="topbar">
|
||||
<!-- 左侧品牌 -->
|
||||
<router-link to="/" class="brand">
|
||||
<div class="brand-mark"></div>
|
||||
<div class="brand-text">科技大脑智慧服务平台</div>
|
||||
</router-link>
|
||||
|
||||
<!-- 右侧整条胶囊导航条 -->
|
||||
<div class="nav-wrap">
|
||||
<div class="nav-list" role="navigation" aria-label="主导航">
|
||||
<router-link to="/" class="nav-item" :class="{ 'is-active': $route.name === 'home' }">首页</router-link>
|
||||
<router-link to="/news-policy" class="nav-item" :class="{ 'is-active': $route.name === 'news-policy' || $route.name === 'news-policy-detail' }">新闻政策</router-link>
|
||||
<router-link to="/smart-qa" class="nav-item" :class="{ 'is-active': $route.name === 'smart-qa' }">智慧问答</router-link>
|
||||
<router-link to="/tech-resources" class="nav-item" :class="{ 'is-active': $route.name === 'tech-resources' }">科技资源</router-link>
|
||||
<router-link to="/talent-profile" class="nav-item" :class="{ 'is-active': $route.name === 'talent-profile' }">人才画像</router-link>
|
||||
<router-link to="/login" class="nav-item" :class="{ 'is-active': $route.name === 'login' }">登录</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 无需额外逻辑
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== 顶栏 ===== */
|
||||
/* 顶部整行:左右两端布局 - 浅色风格 */
|
||||
.topbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
padding: 24px 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
pointer-events: auto;
|
||||
background: #ffffff;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 左侧品牌区 */
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
color: #409eff;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.brand:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 24px;
|
||||
height: 6px;
|
||||
border-radius: 4px;
|
||||
background: #49b0ff;
|
||||
box-shadow: 0 0 8px rgba(73, 176, 255, 0.5);
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-size: 20px;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 右侧胶囊导航条:纯 div,flex 布局 */
|
||||
.nav-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
padding: 10px 10px 10px 18px;
|
||||
min-width: 720px;
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
|
||||
/* 菜单区:居中对齐、等间距 */
|
||||
.nav-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 42px;
|
||||
}
|
||||
|
||||
/* 菜单项:router-link 样式,hover 高亮,active 发光 */
|
||||
.nav-item {
|
||||
position: relative;
|
||||
padding: 6px 2px;
|
||||
color: #606266;
|
||||
font-size: 16px;
|
||||
letter-spacing: 0.5px;
|
||||
transition: color 0.2s ease;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.nav-item.is-active {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,4 +1,11 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
import ElementPlus from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(router)
|
||||
app.use(ElementPlus)
|
||||
app.mount('#app')
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import HomeIndex from '@/views/HomeIndex.vue'
|
||||
import NewsPolicyIndex from '@/views/NewsPolicy/NewsPolicyIndex.vue'
|
||||
import PolicyDetail from '@/views/NewsPolicy/PolicyDetail.vue'
|
||||
import SmartQAIndex from '@/views/SmartQA/SmartQAIndex.vue'
|
||||
import TechResourcesIndex from '@/views/TechResources/TechResourcesIndex.vue'
|
||||
import TalentProfileIndex from '@/views/TalentProfile/TalentProfileIndex.vue'
|
||||
import UserLoginIndex from '@/views/UserLogin/UserLoginIndex.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: HomeIndex
|
||||
},
|
||||
{
|
||||
path: '/news-policy',
|
||||
name: 'news-policy',
|
||||
component: NewsPolicyIndex
|
||||
},
|
||||
{
|
||||
path: '/news-policy/detail/:id',
|
||||
name: 'news-policy-detail',
|
||||
component: PolicyDetail
|
||||
},
|
||||
{
|
||||
path: '/smart-qa',
|
||||
name: 'smart-qa',
|
||||
component: SmartQAIndex
|
||||
},
|
||||
{
|
||||
path: '/tech-resources',
|
||||
name: 'tech-resources',
|
||||
component: TechResourcesIndex
|
||||
},
|
||||
{
|
||||
path: '/talent-profile',
|
||||
name: 'talent-profile',
|
||||
component: TalentProfileIndex
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
component: UserLoginIndex
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
|
@ -0,0 +1,802 @@
|
|||
<template>
|
||||
<div class="screen">
|
||||
<div class="stage">
|
||||
<div class="bg"></div>
|
||||
|
||||
<!-- 顶部导航 -->
|
||||
<header class="topbar">
|
||||
<!-- 左侧品牌 -->
|
||||
<div class="brand">
|
||||
<div class="brand-mark"></div>
|
||||
<div class="brand-text">科技大脑智慧服务平台</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧整条胶囊导航条 -->
|
||||
<div class="nav-wrap">
|
||||
<div class="nav-list" role="navigation" aria-label="主导航">
|
||||
<router-link to="/" class="nav-item" :class="{ 'is-active': $route.name === 'home' }">首页</router-link>
|
||||
<router-link to="/news-policy" class="nav-item" :class="{ 'is-active': $route.name === 'news-policy' }">新闻政策</router-link>
|
||||
<router-link to="/smart-qa" class="nav-item" :class="{ 'is-active': $route.name === 'smart-qa' }">智慧问答</router-link>
|
||||
<router-link to="/tech-resources" class="nav-item" :class="{ 'is-active': $route.name === 'tech-resources' }">科技资源</router-link>
|
||||
<router-link to="/talent-profile" class="nav-item" :class="{ 'is-active': $route.name === 'talent-profile' }">人才画像</router-link>
|
||||
<router-link to="/login" class="nav-item" :class="{ 'is-active': $route.name === 'login' }">登录</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<!-- ===== 核心:大圆风格化(按设计稿) ===== -->
|
||||
<section class="core">
|
||||
<!-- 背景环 -->
|
||||
<div class="ring ring-1"></div>
|
||||
<div class="ring ring-2"></div>
|
||||
<div class="ring ring-3"></div>
|
||||
|
||||
<!-- 大圆(盘面 + 渐变描边 + 文案) -->
|
||||
<div class="center">
|
||||
<div class="center-bezel"></div>
|
||||
<div class="center-disc">
|
||||
<div class="disc-gloss"></div>
|
||||
<div class="disc-vignette"></div>
|
||||
</div>
|
||||
<div class="center-title">科技大脑</div>
|
||||
|
||||
<!-- 一圈蓝色小点(可慢速自转,也可设为静止) -->
|
||||
<ul class="center-dots">
|
||||
<li v-for="n in DOT_COUNT" :key="`cd-${n}`" :style="dotHostStyle(n)">
|
||||
<i class="spark" :style="dotDelayStyle(n)"></i>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- 4 个小三角指示器(缓慢公转、轻微呼吸) -->
|
||||
<ul class="center-tris">
|
||||
<li v-for="t in 4" :key="`tri-${t}`" :style="triStyle(t)"></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 顶层:中心循环发光点(轨道上的亮点) -->
|
||||
<div class="light-sprite"></div>
|
||||
|
||||
<!-- 内部小粒子(淡) -->
|
||||
<ul class="inner-dots">
|
||||
<li v-for="n in 12" :key="`inner-${n}`" :style="innerDotStyle(n)"></li>
|
||||
</ul>
|
||||
|
||||
<!-- ===== 轨道与 5 个功能圆(保持不自转、圆心落在轨道上) ===== -->
|
||||
<div class="orbit">
|
||||
<div class="node" style="--theta: 320deg;">
|
||||
<div class="align">
|
||||
<div class="pos">
|
||||
<router-link to="/news-policy" class="bubble">
|
||||
<div class="dot-ring"></div>
|
||||
<span class="label">新闻<br/>政策</span><i class="marker"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="node" style="--theta: 20deg;">
|
||||
<div class="align">
|
||||
<div class="pos">
|
||||
<router-link to="/smart-qa" class="bubble">
|
||||
<div class="dot-ring"></div>
|
||||
<span class="label">智慧<br/>问答</span><i class="marker"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="node" style="--theta: 70deg;">
|
||||
<div class="align">
|
||||
<div class="pos">
|
||||
<router-link to="/talent-profile" class="bubble">
|
||||
<div class="dot-ring"></div>
|
||||
<span class="label">人才<br/>画像</span><i class="marker"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="node" style="--theta: 210deg;">
|
||||
<div class="align">
|
||||
<div class="pos">
|
||||
<div class="bubble">
|
||||
<div class="dot-ring"></div>
|
||||
<span class="label">科技<br/>地图</span><i class="marker"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="node" style="--theta: 280deg;">
|
||||
<div class="align">
|
||||
<div class="pos">
|
||||
<router-link to="/tech-resources" class="bubble">
|
||||
<div class="dot-ring"></div>
|
||||
<span class="label">科技<br/>资源</span><i class="marker"></i>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 左侧引言 -->
|
||||
<aside class="intro">
|
||||
<div class="intro-title">引言</div>
|
||||
<div class="intro-body">
|
||||
挖掘信息要素价值<br/>图谱 | 图谱 | 脉络<br/>
|
||||
赋能主题检索与决策<br/>帮助企业更快了解<br/>投资机遇!
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- 右侧 AI 浮标 -->
|
||||
<div class="ai-fab">
|
||||
<div class="ai-icon">🤖</div>
|
||||
<div class="ai-text">AI<br/>问答</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部装饰 -->
|
||||
<footer class="progress">
|
||||
<div class="bar bar-left"></div>
|
||||
<div class="bar bar-right"></div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
/** ===== 中心小点圈参数(参考设计) ===== */
|
||||
const DOT_COUNT = 24 // 点的数量(设计上比 36 更稀疏)
|
||||
const DOT_RADIUS = 92 // 点的半径(落在描边内侧)
|
||||
const dotHostStyle = (n: number) => {
|
||||
const deg = (360 / DOT_COUNT) * n
|
||||
return {transform: `rotate(${deg}deg) translateY(-${DOT_RADIUS}px)`}
|
||||
}
|
||||
const dotDelayStyle = (n: number) => {
|
||||
// 三组错峰
|
||||
const delay = (n % 6) * 0.15
|
||||
return {animationDelay: `${delay}s`}
|
||||
}
|
||||
|
||||
/** 四个三角的位置与延迟(在描边外侧一点点) */
|
||||
const TRI_RADIUS = 115
|
||||
const triStyle = (i: number) => {
|
||||
const deg = 90 * (i - 1) // 0,90,180,270
|
||||
const delay = (i - 1) * 0.3
|
||||
return {
|
||||
transform: `rotate(${deg}deg) translateY(-${TRI_RADIUS}px)`,
|
||||
animationDelay: `${delay}s`
|
||||
}
|
||||
}
|
||||
|
||||
/** 内部微粒(淡) */
|
||||
const innerDotStyle = (n: number) => {
|
||||
const deg = (360 / 12) * n
|
||||
return {transform: `rotate(${deg}deg) translateY(-70px)`}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== 一屏缩放 ===== */
|
||||
.screen {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
background: #081a2b;
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.stage {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
margin: 0 auto;
|
||||
transform-origin: 50% 50%
|
||||
}
|
||||
|
||||
@media (min-aspect-ratio: 16/9) {
|
||||
.stage {
|
||||
transform: scale(calc(100vh / 1080))
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-aspect-ratio: 16/9) {
|
||||
.stage {
|
||||
transform: scale(calc(100vw / 1920))
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 背景(渐变+网格) ===== */
|
||||
.bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(1000px 500px at 50% 70%, rgba(23, 94, 150, .35), rgba(8, 26, 43, 0) 60%),
|
||||
linear-gradient(180deg, #0b2238 0%, #061626 60%, #04111f 100%)
|
||||
}
|
||||
|
||||
.bg::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(60% 40% at 50% 70%, rgba(255, 255, 255, .06) 0, rgba(255, 255, 255, 0) 60%),
|
||||
repeating-linear-gradient(to right, rgba(255, 255, 255, .06) 0 1px, transparent 1px 40px),
|
||||
repeating-linear-gradient(to top, rgba(255, 255, 255, .06) 0 1px, transparent 1px 40px);
|
||||
mask: linear-gradient(to top, rgba(0, 0, 0, 0) 0%, #000 35%, #000 100%);
|
||||
transform: perspective(800px) rotateX(65deg) translateY(320px);
|
||||
}
|
||||
|
||||
/* ===== 顶栏 ===== */
|
||||
/* 顶部整行:左右两端布局 */
|
||||
.topbar {
|
||||
position: absolute;
|
||||
inset: 24px 32px auto 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* 左侧品牌区 */
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
color: #cfe8ff
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
width: 24px;
|
||||
height: 6px;
|
||||
border-radius: 4px;
|
||||
background: #49b0ff;
|
||||
box-shadow: 0 0 12px #49b0ff
|
||||
}
|
||||
|
||||
.brand-text {
|
||||
font-size: 20px;
|
||||
letter-spacing: .5px;
|
||||
opacity: .95
|
||||
}
|
||||
|
||||
/* 右侧胶囊导航条:纯 div,flex 布局 */
|
||||
.nav-wrap {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 16px;
|
||||
padding: 10px 10px 10px 18px;
|
||||
min-width: 720px; /* 让条看起来更长一些,可按需调整 */
|
||||
backdrop-filter: blur(2px);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* 菜单区:居中对齐、等间距 */
|
||||
.nav-list {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 42px;
|
||||
}
|
||||
|
||||
/* 菜单项:router-link 样式,hover 高亮,active 发光 */
|
||||
.nav-item {
|
||||
position: relative;
|
||||
padding: 6px 2px;
|
||||
color: #ffffff;
|
||||
font-size: 16px;
|
||||
letter-spacing: .5px;
|
||||
transition: color .2s ease, text-shadow .2s ease, transform .2s ease;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
color: #d7ecff;
|
||||
text-shadow: 0 0 10px rgba(144, 210, 255, .5);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-item.is-active {
|
||||
color: #8fd3ff;
|
||||
font-weight: 600;
|
||||
text-shadow: 0 0 10px rgba(150, 210, 255, .85);
|
||||
}
|
||||
|
||||
/* 登录按钮(右侧圆角按钮) */
|
||||
.nav-login {
|
||||
padding: 6px 16px;
|
||||
border-radius: 10px;
|
||||
color: #e6f5ff;
|
||||
font-size: 14px;
|
||||
letter-spacing: .5px;
|
||||
border: 1px solid rgba(130, 196, 255, .35);
|
||||
background: linear-gradient(180deg, #163a57, #0c2135);
|
||||
box-shadow: inset 0 0 12px rgba(73, 176, 255, .18), 0 6px 16px rgba(6, 22, 38, .35);
|
||||
transition: transform .15s ease, box-shadow .2s ease;
|
||||
}
|
||||
|
||||
.nav-login:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: inset 0 0 12px rgba(73, 176, 255, .25), 0 10px 20px rgba(6, 22, 38, .45)
|
||||
}
|
||||
|
||||
|
||||
/* ===== 核心(同心) ===== */
|
||||
.core {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 720px;
|
||||
height: 720px
|
||||
}
|
||||
|
||||
.ring {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(130, 196, 255, .12)
|
||||
}
|
||||
|
||||
.ring-1 {
|
||||
transform: scale(.78)
|
||||
}
|
||||
|
||||
.ring-2 {
|
||||
transform: scale(.60);
|
||||
border-color: rgba(130, 196, 255, .2)
|
||||
}
|
||||
|
||||
.ring-3 {
|
||||
transform: scale(.43);
|
||||
border-color: rgba(130, 196, 255, .28)
|
||||
}
|
||||
|
||||
/* ===== 中心大圆(设计还原) ===== */
|
||||
.center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.center-bezel {
|
||||
position: absolute;
|
||||
inset: 10px;
|
||||
border-radius: 50%;
|
||||
/* 渐变描边:内外发光 + 2px 蓝色描边 */
|
||||
background: radial-gradient(circle at 50% 50%, rgba(86, 184, 255, .9), rgba(86, 184, 255, 0) 60%),
|
||||
conic-gradient(from 0deg, rgba(86, 184, 255, .9), #5db9ff, #5db9ff, rgba(86, 184, 255, .9));
|
||||
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 0); /* 4px 描边 */
|
||||
mask: radial-gradient(farthest-side, transparent calc(100% - 4px), #000 0);
|
||||
filter: drop-shadow(0 0 10px rgba(86, 184, 255, .55));
|
||||
}
|
||||
|
||||
.center-disc {
|
||||
position: absolute;
|
||||
inset: 28px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
background: radial-gradient(140px 120px at 50% 30%, rgba(120, 200, 255, .35), rgba(120, 200, 255, 0) 70%),
|
||||
radial-gradient(circle at 50% 60%, #1b4d76 0%, #0d2e4a 60%, #07263f 100%);
|
||||
box-shadow: inset 0 8px 20px rgba(255, 255, 255, .16),
|
||||
inset 0 -14px 22px rgba(0, 0, 0, .35);
|
||||
}
|
||||
|
||||
.disc-gloss {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(180px 70px at 50% 12%, rgba(255, 255, 255, .4), rgba(255, 255, 255, 0) 60%);
|
||||
mix-blend-mode: screen;
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.disc-vignette {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(70% 80% at 50% 100%, rgba(0, 0, 0, .3), rgba(0, 0, 0, 0) 60%);
|
||||
mix-blend-mode: multiply;
|
||||
opacity: .35;
|
||||
}
|
||||
|
||||
.center-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #f5fbff;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 0 0 8px rgba(160, 210, 255, .9);
|
||||
}
|
||||
|
||||
/* 点环:父层定位 + 子层闪烁,环整体慢速转(更灵动;不想转可去掉 animation) */
|
||||
.center-dots {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
animation: slowSpin 60s linear infinite
|
||||
}
|
||||
|
||||
.center-dots li {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform-origin: 0 0
|
||||
}
|
||||
|
||||
.center-dots .spark {
|
||||
display: block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin: -4px 0 0 -4px;
|
||||
border-radius: 50%;
|
||||
background: #9cd6ff;
|
||||
filter: drop-shadow(0 0 6px #5db9ff) drop-shadow(0 0 10px #5db9ff);
|
||||
animation: sparkTwinkle 2.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes sparkTwinkle {
|
||||
0%, 100% {
|
||||
opacity: .25;
|
||||
transform: scale(.85)
|
||||
}
|
||||
50% {
|
||||
opacity: .95;
|
||||
transform: scale(1.2)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slowSpin {
|
||||
to {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
/* 四个小三角指示器 */
|
||||
.center-tris {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
animation: trisSpin 24s linear infinite
|
||||
}
|
||||
|
||||
.center-tris li {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.center-tris li::after {
|
||||
content: "";
|
||||
display: block;
|
||||
transform: translate(-6px, -8px);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 10px solid #8fd3ff;
|
||||
filter: drop-shadow(0 0 4px rgba(100, 190, 255, .9));
|
||||
opacity: .8;
|
||||
animation: triPulse 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes trisSpin {
|
||||
to {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes triPulse {
|
||||
0%, 100% {
|
||||
opacity: .45;
|
||||
transform: translate(-6px, -8px) scale(.9)
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translate(-6px, -8px) scale(1.1)
|
||||
}
|
||||
}
|
||||
|
||||
/* 中心循环发光点(轨道上的亮点) */
|
||||
.light-sprite {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: -9px 0 0 -9px;
|
||||
border-radius: 50%;
|
||||
transform: translate(-185px, -205px);
|
||||
background: radial-gradient(circle, #bfe6ff 0 30%, rgba(191, 230, 255, 0) 70%), radial-gradient(circle, #5cc8ff 0 40%, rgba(92, 200, 255, 0) 65%);
|
||||
filter: drop-shadow(0 0 8px #7fd2ff) drop-shadow(0 0 16px #7fd2ff);
|
||||
animation: spriteBlink 1.2s ease-in-out infinite
|
||||
}
|
||||
|
||||
@keyframes spriteBlink {
|
||||
0%, 100% {
|
||||
transform: translate(-185px, -205px) scale(.6);
|
||||
opacity: .25
|
||||
}
|
||||
50% {
|
||||
transform: translate(-185px, -205px) scale(1.25);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
/* 内部小粒子(淡) */
|
||||
.inner-dots {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
animation: innerSpin 30s linear infinite
|
||||
}
|
||||
|
||||
.inner-dots li {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
margin: -2.5px 0 0 -2.5px;
|
||||
border-radius: 50%;
|
||||
background: #aee1ff;
|
||||
opacity: .5;
|
||||
filter: drop-shadow(0 0 4px #62c2ff);
|
||||
transform-origin: 0 0
|
||||
}
|
||||
|
||||
@keyframes innerSpin {
|
||||
to {
|
||||
transform: rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 轨道与功能圆保持不变(关键:圆心落在轨道上、文字不歪) ===== */
|
||||
.orbit {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 560px;
|
||||
height: 560px;
|
||||
border-radius: 50%;
|
||||
animation: spin 36s linear infinite;
|
||||
box-shadow: 0 0 0 1px rgba(130, 196, 255, .1) inset, 0 0 22px rgba(73, 176, 255, .05) inset
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: translate(-50%, -50%) rotate(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
.node {
|
||||
--radius: 290px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: rotate(var(--theta)) translate(var(--radius));
|
||||
transform-origin: 0 0
|
||||
}
|
||||
|
||||
.align {
|
||||
transform: rotate(calc(-1 * var(--theta)));
|
||||
transform-origin: 0 0
|
||||
}
|
||||
|
||||
.pos {
|
||||
position: relative;
|
||||
transform: translate(-50%, -50%)
|
||||
}
|
||||
|
||||
/* 功能小圆主体 */
|
||||
.bubble{
|
||||
position: relative;
|
||||
width: 138px;
|
||||
height: 138px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
/* 仅这两点:不透明背景色 + 底部黑色阴影 */
|
||||
background-color: #0B2444;
|
||||
box-shadow: 0 18px 28px rgba(0, 0, 0, 0.55); /* 底部黑色阴影 */
|
||||
animation: counterSpin 36s linear infinite; /* 如果你还在用轨道反转动画 */
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.bubble:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 20px 35px rgba(0, 0, 0, 0.65), 0 0 20px rgba(73, 176, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 外圈柔和发光(薄描边 + 外发光) */
|
||||
.bubble::before{
|
||||
content:"";
|
||||
position:absolute; inset:10px; border-radius:50%;
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%, rgba(86,184,255,.9), rgba(86,184,255,0) 60%);
|
||||
/* 只保留2px的“环” */
|
||||
-webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 0);
|
||||
mask: radial-gradient(farthest-side, transparent calc(100% - 2px), #000 0);
|
||||
filter: drop-shadow(0 2px 8px rgba(86,184,255,.45));
|
||||
opacity:.55;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
/* 顶部镜面高光(柔和过渡,增加玻璃质感) */
|
||||
.bubble::after{
|
||||
content:"";
|
||||
position:absolute; inset:0; border-radius:50%;
|
||||
background:
|
||||
radial-gradient(160px 120px at 50% 22%, rgba(255,255,255,.32), rgba(255,255,255,0) 60%);
|
||||
mix-blend-mode: screen; /* 让高光更柔 */
|
||||
opacity:.35;
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
/* 如果你的蓝色箭头在底部:保持不转动 */
|
||||
.marker{
|
||||
position:absolute; left:50%; bottom:14px; transform:translateX(-50%);
|
||||
width:0; height:0;
|
||||
border-left:6px solid transparent; border-right:6px solid transparent;
|
||||
border-bottom:10px solid #27b9ff;
|
||||
filter: drop-shadow(0 0 4px rgba(73,176,255,.9));
|
||||
}
|
||||
|
||||
/* (可选)整个小圆再来一点环境投影,更贴设计稿的“厚度” */
|
||||
.bubble-wrap{ /* 如果外面还有一层容器,可加在容器上 */
|
||||
filter: drop-shadow(0 10px 12px rgba(4,17,31,.55));
|
||||
}
|
||||
|
||||
|
||||
@keyframes counterSpin {
|
||||
to {
|
||||
transform: rotate(-360deg)
|
||||
}
|
||||
}
|
||||
|
||||
.dot-ring {
|
||||
position: absolute;
|
||||
inset: 12px;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
background: url("data:image/svg+xml;utf8,\
|
||||
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 138 138'>\
|
||||
<defs><filter id='g' x='-50%' y='-50%' width='200%' height='200%'>\
|
||||
<feGaussianBlur stdDeviation='0.9' result='b'/><feMerge><feMergeNode in='b'/><feMergeNode in='SourceGraphic'/></feMerge>\
|
||||
</filter></defs>\
|
||||
<circle cx='69' cy='69' r='53' fill='none' stroke='rgba(214,234,255,0.95)' stroke-width='2' stroke-linecap='round' stroke-dasharray='0 14' filter='url(%23g)'/>\
|
||||
</svg>") center/contain no-repeat;
|
||||
opacity: .95
|
||||
}
|
||||
|
||||
.label {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
color: #f2fbff;
|
||||
font-size: 20px;
|
||||
line-height: 1.15;
|
||||
letter-spacing: 1px;
|
||||
text-shadow: 0 1px 0 rgba(0, 0, 0, .4), 0 0 6px rgba(173, 220, 255, .35)
|
||||
}
|
||||
|
||||
.marker {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 14px;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 10px solid #27b9ff;
|
||||
filter: drop-shadow(0 0 4px rgba(73, 176, 255, .9))
|
||||
}
|
||||
|
||||
/* ===== 侧边与底部装饰 ===== */
|
||||
.intro {
|
||||
position: absolute;
|
||||
left: 72px;
|
||||
top: 418px;
|
||||
width: 240px;
|
||||
padding: 18px 16px 20px;
|
||||
color: #cfe8ff;
|
||||
border: 1px solid rgba(130, 196, 255, .25);
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, rgba(20, 54, 88, .25), rgba(8, 24, 41, .25));
|
||||
box-shadow: 0 0 18px rgba(73, 176, 255, .12), inset 0 0 24px rgba(173, 220, 255, .08)
|
||||
}
|
||||
|
||||
.intro-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
color: #8fd3ff;
|
||||
letter-spacing: 2px
|
||||
}
|
||||
|
||||
.intro-body {
|
||||
font-size: 12px;
|
||||
opacity: .9
|
||||
}
|
||||
|
||||
.ai-fab {
|
||||
position: absolute;
|
||||
right: 56px;
|
||||
top: 560px;
|
||||
width: 78px;
|
||||
height: 140px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(130, 196, 255, .35);
|
||||
background: linear-gradient(180deg, rgba(19, 51, 82, .55), rgba(9, 26, 44, .55));
|
||||
color: #e6f5ff;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 6px;
|
||||
box-shadow: 0 0 18px rgba(73, 176, 255, .2)
|
||||
}
|
||||
|
||||
.ai-icon {
|
||||
font-size: 28px;
|
||||
filter: drop-shadow(0 0 6px rgba(73, 176, 255, .8))
|
||||
}
|
||||
|
||||
.ai-text {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 1.2
|
||||
}
|
||||
|
||||
.progress {
|
||||
position: absolute;
|
||||
left: 60px;
|
||||
right: 60px;
|
||||
bottom: 56px;
|
||||
height: 16px
|
||||
}
|
||||
|
||||
.bar {
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(90deg, #93d2ff, #1a73b6);
|
||||
box-shadow: 0 0 12px rgba(73, 176, 255, .6)
|
||||
}
|
||||
|
||||
.bar-left {
|
||||
left: 0;
|
||||
width: 560px
|
||||
}
|
||||
|
||||
.bar-right {
|
||||
right: 0;
|
||||
width: 560px
|
||||
}
|
||||
|
||||
:where(.stage) {
|
||||
font-family: "PingFang SC", "Microsoft YaHei", system-ui, -apple-system, Segoe UI, Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,459 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<PageNavigation />
|
||||
|
||||
<div class="main-content">
|
||||
<div class="page-header">
|
||||
<h1>新闻政策</h1>
|
||||
<p>最新科技政策资讯与行业动态</p>
|
||||
</div>
|
||||
|
||||
<!-- 标签页切换 -->
|
||||
<div class="tabs-container">
|
||||
<el-tabs v-model="activeTab" class="news-tabs" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="政策法规" name="policy">
|
||||
<div class="news-grid">
|
||||
<div v-for="item in policyNews" :key="item.id" class="news-card" @click="goToDetail(item.id)">
|
||||
<div class="news-image">
|
||||
<div class="news-placeholder">{{ item.type }}</div>
|
||||
<el-tag v-if="item.isNew" type="success" size="small" class="news-tag">最新</el-tag>
|
||||
</div>
|
||||
<div class="news-content">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p>{{ item.summary }}</p>
|
||||
<div class="news-meta">
|
||||
<span class="date">{{ item.date }}</span>
|
||||
<span class="views">{{ item.views }} 次浏览</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="热点新闻" name="news">
|
||||
<div class="news-grid">
|
||||
<div v-for="item in hotNews" :key="item.id" class="news-card" @click="goToDetail(item.id)">
|
||||
<div class="news-image">
|
||||
<div class="news-placeholder">{{ item.type }}</div>
|
||||
<el-tag v-if="item.isHot" type="danger" size="small" class="news-tag">热门</el-tag>
|
||||
</div>
|
||||
<div class="news-content">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p>{{ item.summary }}</p>
|
||||
<div class="news-meta">
|
||||
<span class="date">{{ item.date }}</span>
|
||||
<span class="views">{{ item.views }} 次浏览</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
:page-size="pageSize"
|
||||
:total="totalItems"
|
||||
layout="prev, pager, next, jumper"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<PageFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import PageNavigation from '@/components/PageNavigation.vue'
|
||||
import PageFooter from '@/components/PageFooter.vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
interface NewsItem {
|
||||
id: string
|
||||
title: string
|
||||
summary: string
|
||||
type: string
|
||||
date: string
|
||||
views: number
|
||||
isNew?: boolean
|
||||
isHot?: boolean
|
||||
}
|
||||
|
||||
const activeTab = ref('policy')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(9)
|
||||
const totalItems = ref(27)
|
||||
|
||||
const policyNews = ref<NewsItem[]>([
|
||||
{
|
||||
id: 'policy-1',
|
||||
title: '科技创新支持政策发布',
|
||||
summary: '为进一步推动科技创新发展,相关部门发布了一系列支持政策,涵盖资金支持、税收优惠等多个方面。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-15',
|
||||
views: 1250,
|
||||
isNew: true
|
||||
},
|
||||
{
|
||||
id: 'policy-2',
|
||||
title: '数字化转型指导意见',
|
||||
summary: '加快推进企业数字化转型,提升科技创新能力,构建数字经济新优势。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-12',
|
||||
views: 980
|
||||
},
|
||||
{
|
||||
id: 'policy-3',
|
||||
title: '高新技术企业认定管理办法',
|
||||
summary: '详细解读高新技术企业认定的条件和流程,为企业申报提供指导。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-10',
|
||||
views: 1580
|
||||
},
|
||||
{
|
||||
id: 'policy-4',
|
||||
title: '科技成果转化激励政策',
|
||||
summary: '促进科技成果转化的相关激励措施和支持政策,推动产学研深度融合。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-08',
|
||||
views: 720
|
||||
},
|
||||
{
|
||||
id: 'policy-5',
|
||||
title: '人才引进优惠政策',
|
||||
summary: '针对高层次人才引进的住房补贴、子女教育等配套优惠政策。',
|
||||
type: 'PRESS',
|
||||
date: '2024-01-05',
|
||||
views: 890
|
||||
},
|
||||
{
|
||||
id: 'policy-6',
|
||||
title: '创新创业扶持措施',
|
||||
summary: '支持大众创业万众创新,提供资金、场地、服务等全方位扶持。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-03',
|
||||
views: 1120
|
||||
},
|
||||
{
|
||||
id: 'policy-7',
|
||||
title: '知识产权保护条例',
|
||||
summary: '加强知识产权保护,营造良好的创新环境和营商环境。',
|
||||
type: 'PRESS',
|
||||
date: '2024-01-01',
|
||||
views: 650
|
||||
},
|
||||
{
|
||||
id: 'policy-8',
|
||||
title: '产业发展引导基金',
|
||||
summary: '设立产业发展引导基金,支持战略性新兴产业发展。',
|
||||
type: 'NEWS',
|
||||
date: '2023-12-28',
|
||||
views: 1350
|
||||
},
|
||||
{
|
||||
id: 'policy-9',
|
||||
title: '科研项目管理规定',
|
||||
summary: '规范科研项目立项、实施、验收等全过程管理。',
|
||||
type: 'PRESS',
|
||||
date: '2023-12-25',
|
||||
views: 780
|
||||
}
|
||||
])
|
||||
|
||||
const hotNews = ref<NewsItem[]>([
|
||||
{
|
||||
id: 'news-1',
|
||||
title: '人工智能产业发展迅猛',
|
||||
summary: '我国人工智能产业规模持续扩大,技术创新能力不断提升。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-14',
|
||||
views: 2150,
|
||||
isHot: true
|
||||
},
|
||||
{
|
||||
id: 'news-2',
|
||||
title: '新能源汽车销量创新高',
|
||||
summary: '2023年新能源汽车销量突破800万辆,产业发展势头强劲。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-13',
|
||||
views: 1890
|
||||
},
|
||||
{
|
||||
id: 'news-3',
|
||||
title: '5G技术应用场景不断拓展',
|
||||
summary: '5G技术在工业互联网、智慧城市等领域应用日益广泛。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-11',
|
||||
views: 1420
|
||||
},
|
||||
{
|
||||
id: 'news-4',
|
||||
title: '生物医药创新成果丰硕',
|
||||
summary: '我国生物医药领域取得多项重大突破,创新药物研发加速。',
|
||||
type: 'PRESS',
|
||||
date: '2024-01-09',
|
||||
views: 1680,
|
||||
isHot: true
|
||||
},
|
||||
{
|
||||
id: 'news-5',
|
||||
title: '量子计算技术获得突破',
|
||||
summary: '国内量子计算研究取得重要进展,技术水平达到国际先进。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-07',
|
||||
views: 2300
|
||||
},
|
||||
{
|
||||
id: 'news-6',
|
||||
title: '绿色低碳技术快速发展',
|
||||
summary: '碳达峰碳中和目标推动绿色低碳技术创新和产业发展。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-06',
|
||||
views: 1150
|
||||
},
|
||||
{
|
||||
id: 'news-7',
|
||||
title: '芯片产业自主创新加速',
|
||||
summary: '国产芯片技术不断突破,产业链自主可控能力持续提升。',
|
||||
type: 'PRESS',
|
||||
date: '2024-01-04',
|
||||
views: 1950
|
||||
},
|
||||
{
|
||||
id: 'news-8',
|
||||
title: '航空航天领域成就显著',
|
||||
summary: '我国航空航天事业取得历史性成就,技术实力不断增强。',
|
||||
type: 'NEWS',
|
||||
date: '2024-01-02',
|
||||
views: 1720
|
||||
},
|
||||
{
|
||||
id: 'news-9',
|
||||
title: '数字经济规模持续扩大',
|
||||
summary: '数字经济成为经济增长新引擎,规模占GDP比重不断提升。',
|
||||
type: 'PRESS',
|
||||
date: '2023-12-30',
|
||||
views: 1380
|
||||
}
|
||||
])
|
||||
|
||||
const handleTabChange = (tabName: string) => {
|
||||
currentPage.value = 1
|
||||
console.log('切换到标签页:', tabName)
|
||||
}
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('切换到第', page, '页')
|
||||
}
|
||||
|
||||
const goToDetail = (id: string) => {
|
||||
router.push(`/news-policy/detail/${id}`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
color: #303133;
|
||||
padding: 80px 0 0;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px 60px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 48px;
|
||||
color: #409eff;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
background: #ffffff;
|
||||
border-radius: 16px;
|
||||
padding: 24px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 30px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.news-tabs {
|
||||
--el-tabs-header-height: 50px;
|
||||
}
|
||||
|
||||
.news-tabs :deep(.el-tabs__header) {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.news-tabs :deep(.el-tabs__nav-wrap::after) {
|
||||
height: 1px;
|
||||
background-color: #e4e7ed;
|
||||
}
|
||||
|
||||
.news-tabs :deep(.el-tabs__item) {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
.news-tabs :deep(.el-tabs__item.is-active) {
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.news-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 24px;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.news-card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.news-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.news-image {
|
||||
position: relative;
|
||||
height: 120px;
|
||||
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.news-placeholder {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.news-tag {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
.news-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.news-content h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.news-content p {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 16px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.news-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.date {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.views {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.pagination-container :deep(.el-pagination) {
|
||||
--el-pagination-font-size: 14px;
|
||||
--el-pagination-bg-color: #ffffff;
|
||||
--el-pagination-text-color: #606266;
|
||||
--el-pagination-border-radius: 8px;
|
||||
}
|
||||
|
||||
.pagination-container :deep(.el-pager li) {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
margin: 0 4px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.pagination-container :deep(.el-pager li.is-active) {
|
||||
background-color: #409eff;
|
||||
border-color: #409eff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.news-grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.news-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 0 16px 40px;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
padding: 70px 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<PageNavigation />
|
||||
|
||||
<div class="detail-container">
|
||||
<div class="breadcrumb">
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item>
|
||||
<router-link to="/">首页</router-link>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>
|
||||
<router-link to="/news-policy">新闻政策</router-link>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item>政策详情</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
||||
<el-card class="detail-card" shadow="never">
|
||||
<template #header>
|
||||
<div class="detail-header">
|
||||
<h1>{{ policyDetail.title }}</h1>
|
||||
<div class="meta-info">
|
||||
<el-tag :type="policyDetail.tagType" size="small">{{ policyDetail.tag }}</el-tag>
|
||||
<span class="publish-date">发布时间:{{ policyDetail.publishDate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="detail-content">
|
||||
<div class="content-section">
|
||||
<h2>政策概述</h2>
|
||||
<p>{{ policyDetail.summary }}</p>
|
||||
</div>
|
||||
|
||||
<div class="content-section">
|
||||
<h2>主要内容</h2>
|
||||
<div v-html="policyDetail.content"></div>
|
||||
</div>
|
||||
|
||||
<div class="content-section">
|
||||
<h2>适用范围</h2>
|
||||
<ul>
|
||||
<li v-for="scope in policyDetail.scopes" :key="scope">{{ scope }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content-section">
|
||||
<h2>申请流程</h2>
|
||||
<el-steps :active="policyDetail.steps.length" finish-status="success">
|
||||
<el-step
|
||||
v-for="(step, index) in policyDetail.steps"
|
||||
:key="index"
|
||||
:title="step.title"
|
||||
:description="step.description"
|
||||
/>
|
||||
</el-steps>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-actions">
|
||||
<el-button type="primary" size="large" @click="downloadPolicy">
|
||||
<el-icon><Download /></el-icon>
|
||||
下载政策文件
|
||||
</el-button>
|
||||
<el-button size="large" @click="goBack">返回列表</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<PageFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import PageNavigation from '@/components/PageNavigation.vue'
|
||||
import PageFooter from '@/components/PageFooter.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
interface PolicyDetail {
|
||||
id: string
|
||||
title: string
|
||||
tag: string
|
||||
tagType: string
|
||||
publishDate: string
|
||||
summary: string
|
||||
content: string
|
||||
scopes: string[]
|
||||
steps: Array<{
|
||||
title: string
|
||||
description: string
|
||||
}>
|
||||
}
|
||||
|
||||
const policyDetail = ref<PolicyDetail>({
|
||||
id: '',
|
||||
title: '',
|
||||
tag: '',
|
||||
tagType: 'info',
|
||||
publishDate: '',
|
||||
summary: '',
|
||||
content: '',
|
||||
scopes: [],
|
||||
steps: []
|
||||
})
|
||||
|
||||
const loadPolicyDetail = () => {
|
||||
const id = route.params.id as string
|
||||
|
||||
// 模拟数据,实际项目中应该从API获取
|
||||
const mockData: Record<string, PolicyDetail> = {
|
||||
'policy-1': {
|
||||
id: 'policy-1',
|
||||
title: '科技创新支持政策发布',
|
||||
tag: '最新',
|
||||
tagType: 'success',
|
||||
publishDate: '2024-01-15',
|
||||
summary: '为进一步推动科技创新发展,相关部门发布了一系列支持政策,涵盖资金支持、税收优惠、人才引进等多个方面。',
|
||||
content: `
|
||||
<p>本政策旨在通过多维度的支持措施,激发企业创新活力,提升科技创新能力。</p>
|
||||
<h3>资金支持</h3>
|
||||
<p>设立专项资金,对符合条件的科技创新项目给予资金支持,最高可达500万元。</p>
|
||||
<h3>税收优惠</h3>
|
||||
<p>享受研发费用加计扣除、高新技术企业所得税优惠等政策。</p>
|
||||
<h3>人才政策</h3>
|
||||
<p>对引进的高层次人才给予住房补贴、子女教育等配套支持。</p>
|
||||
`,
|
||||
scopes: [
|
||||
'高新技术企业',
|
||||
'科技型中小企业',
|
||||
'新型研发机构',
|
||||
'科技服务机构'
|
||||
],
|
||||
steps: [
|
||||
{ title: '申请准备', description: '准备相关材料和证明文件' },
|
||||
{ title: '在线申报', description: '通过官方平台提交申请' },
|
||||
{ title: '专家评审', description: '组织专家进行项目评审' },
|
||||
{ title: '公示公告', description: '评审结果公示' },
|
||||
{ title: '资金拨付', description: '审核通过后拨付资金' }
|
||||
]
|
||||
},
|
||||
'policy-2': {
|
||||
id: 'policy-2',
|
||||
title: '数字化转型指导意见',
|
||||
tag: '热门',
|
||||
tagType: 'info',
|
||||
publishDate: '2024-01-12',
|
||||
summary: '加快推进企业数字化转型,提升科技创新能力,构建数字经济新优势。',
|
||||
content: `
|
||||
<p>数字化转型是企业适应数字经济发展的必然选择。</p>
|
||||
<h3>转型目标</h3>
|
||||
<p>到2025年,重点行业数字化转型取得明显成效。</p>
|
||||
<h3>支持措施</h3>
|
||||
<p>提供技术指导、资金支持、人才培训等全方位服务。</p>
|
||||
`,
|
||||
scopes: [
|
||||
'制造业企业',
|
||||
'服务业企业',
|
||||
'传统行业企业'
|
||||
],
|
||||
steps: [
|
||||
{ title: '现状评估', description: '评估企业数字化现状' },
|
||||
{ title: '方案制定', description: '制定数字化转型方案' },
|
||||
{ title: '试点实施', description: '选择重点领域试点' },
|
||||
{ title: '全面推广', description: '在全企业范围推广' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
policyDetail.value = mockData[id] || mockData['policy-1']
|
||||
}
|
||||
|
||||
const downloadPolicy = () => {
|
||||
ElMessage.success('政策文件下载中...')
|
||||
}
|
||||
|
||||
const goBack = () => {
|
||||
router.push('/news-policy')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadPolicyDetail()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
color: #303133;
|
||||
padding: 80px 20px 0;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.breadcrumb {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.detail-header h1 {
|
||||
font-size: 32px;
|
||||
color: #303133;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.meta-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.publish-date {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.content-section {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.content-section h2 {
|
||||
color: #409eff;
|
||||
font-size: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
border-left: 4px solid #409eff;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.content-section h3 {
|
||||
color: #606266;
|
||||
font-size: 18px;
|
||||
margin: 16px 0 8px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-section p {
|
||||
color: #606266;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.content-section ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.content-section li {
|
||||
color: #606266;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
padding-top: 24px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,287 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<PageNavigation />
|
||||
|
||||
<div class="qa-interface">
|
||||
<div class="chat-container">
|
||||
<div class="messages" ref="messagesContainer">
|
||||
<div v-for="message in messages" :key="message.id"
|
||||
:class="['message', message.type]">
|
||||
<div class="message-content">
|
||||
<div class="avatar">
|
||||
<span v-if="message.type === 'user'">👤</span>
|
||||
<span v-else>🤖</span>
|
||||
</div>
|
||||
<div class="text">{{ message.text }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-area">
|
||||
<div class="input-container">
|
||||
<el-input
|
||||
v-model="currentQuestion"
|
||||
@keyup.enter="sendMessage"
|
||||
placeholder="请输入您的问题..."
|
||||
size="large"
|
||||
clearable
|
||||
/>
|
||||
<el-button
|
||||
@click="sendMessage"
|
||||
type="primary"
|
||||
size="large"
|
||||
:loading="isLoading"
|
||||
:icon="isLoading ? '' : 'ChatDotRound'"
|
||||
>
|
||||
{{ isLoading ? '发送中...' : '发送' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="quick-questions">
|
||||
<h3>常见问题</h3>
|
||||
<div class="question-tags">
|
||||
<button
|
||||
v-for="question in quickQuestions"
|
||||
:key="question"
|
||||
@click="selectQuickQuestion(question)"
|
||||
class="question-tag"
|
||||
>
|
||||
{{ question }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页脚 -->
|
||||
<PageFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, nextTick } from 'vue'
|
||||
import PageNavigation from '@/components/PageNavigation.vue'
|
||||
import PageFooter from '@/components/PageFooter.vue'
|
||||
|
||||
interface Message {
|
||||
id: number
|
||||
type: 'user' | 'ai'
|
||||
text: string
|
||||
}
|
||||
|
||||
const currentQuestion = ref('')
|
||||
const messages = ref<Message[]>([
|
||||
{
|
||||
id: 1,
|
||||
type: 'ai',
|
||||
text: '您好!我是智慧问答助手,有什么可以帮助您的吗?'
|
||||
}
|
||||
])
|
||||
|
||||
const quickQuestions = ref([
|
||||
'如何申请高新技术企业认定?',
|
||||
'科技成果转化有哪些支持政策?',
|
||||
'人才引进政策有哪些?',
|
||||
'科技项目申报流程是什么?',
|
||||
'知识产权保护相关政策',
|
||||
'创新创业扶持措施'
|
||||
])
|
||||
|
||||
const messagesContainer = ref<HTMLElement>()
|
||||
const isLoading = ref(false)
|
||||
|
||||
const sendMessage = async () => {
|
||||
if (!currentQuestion.value.trim()) return
|
||||
|
||||
const userMessage: Message = {
|
||||
id: Date.now(),
|
||||
type: 'user',
|
||||
text: currentQuestion.value
|
||||
}
|
||||
|
||||
messages.value.push(userMessage)
|
||||
const question = currentQuestion.value
|
||||
currentQuestion.value = ''
|
||||
isLoading.value = true
|
||||
|
||||
scrollToBottom()
|
||||
|
||||
// 模拟AI回复
|
||||
setTimeout(() => {
|
||||
const aiResponse: Message = {
|
||||
id: Date.now() + 1,
|
||||
type: 'ai',
|
||||
text: getAIResponse(question)
|
||||
}
|
||||
messages.value.push(aiResponse)
|
||||
isLoading.value = false
|
||||
scrollToBottom()
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
const selectQuickQuestion = (question: string) => {
|
||||
currentQuestion.value = question
|
||||
sendMessage()
|
||||
}
|
||||
|
||||
const getAIResponse = (question: string): string => {
|
||||
// 简单的问答逻辑
|
||||
if (question.includes('高新技术企业')) {
|
||||
return '高新技术企业认定需要满足以下条件:1. 企业申请认定时须注册成立一年以上;2. 企业通过自主研发、受让、受赠、并购等方式,获得对其主要产品(服务)在技术上发挥核心支持作用的知识产权的所有权...'
|
||||
} else if (question.includes('科技成果转化')) {
|
||||
return '科技成果转化支持政策包括:1. 财政资金支持;2. 税收优惠政策;3. 金融支持措施;4. 人才激励政策等。具体可以申请科技成果转化引导基金、享受技术转让所得税优惠等。'
|
||||
} else if (question.includes('人才引进')) {
|
||||
return '人才引进政策主要包括:1. 高层次人才引进计划;2. 住房补贴和安家费;3. 子女教育优待;4. 医疗保障;5. 科研启动资金支持等。不同层次人才享受不同标准的政策支持。'
|
||||
}
|
||||
return '感谢您的提问!这是一个很好的问题。建议您联系相关部门获取更详细的信息,或者查阅最新的政策文件。如果您有其他问题,我很乐意为您解答。'
|
||||
}
|
||||
|
||||
const scrollToBottom = () => {
|
||||
nextTick(() => {
|
||||
if (messagesContainer.value) {
|
||||
messagesContainer.value.scrollTop = messagesContainer.value.scrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
color: #303133;
|
||||
padding: 80px 20px 0;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 48px;
|
||||
color: #409eff;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
font-size: 18px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.qa-interface {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 40px;
|
||||
max-width: 1400px;
|
||||
margin: 40px auto 0;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.chat-container {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.messages {
|
||||
flex: 1;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
max-height: 480px;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.message.user .message-content {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
background: #e4e7ed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.text {
|
||||
background: #f0f2f5;
|
||||
padding: 12px 16px;
|
||||
border-radius: 12px;
|
||||
max-width: 70%;
|
||||
line-height: 1.5;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.message.user .text {
|
||||
background: #409eff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.input-area {
|
||||
padding: 20px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.quick-questions {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 16px;
|
||||
padding: 30px;
|
||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.quick-questions h3 {
|
||||
color: #409eff;
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.question-tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.question-tag {
|
||||
padding: 12px 16px;
|
||||
background: #f0f2f5;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
color: #303133;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.question-tag:hover {
|
||||
border-color: #409eff;
|
||||
background: rgba(64, 158, 255, 0.1);
|
||||
transform: translateX(4px);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<PageNavigation />
|
||||
|
||||
<div class="talent-layout">
|
||||
<!-- 左侧人物列表 -->
|
||||
<div class="talent-sidebar">
|
||||
<div class="sidebar-header"><h3>人物名单</h3></div>
|
||||
<div class="talent-list">
|
||||
<div
|
||||
v-for="person in talentList"
|
||||
:key="person.id"
|
||||
:class="['talent-item', { active: selectedTalent?.id === person.id }]"
|
||||
@click="selectTalent(person)"
|
||||
>
|
||||
<div class="talent-name-main">{{ person.name }}</div>
|
||||
<div class="talent-title-small">({{ person.title }})</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间个人详情 -->
|
||||
<div class="talent-detail">
|
||||
<div v-if="selectedTalent" class="detail-content">
|
||||
<!-- 基本信息 -->
|
||||
<div class="basic-info-section">
|
||||
<div class="avatar-section">
|
||||
<div class="detail-avatar">
|
||||
<img src="http://cdn.rayrayray.cn/avatar.png" :alt="selectedTalent.name" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-grid">
|
||||
<div class="info-row">
|
||||
<div class="info-item"><span class="label">姓名:</span><span class="value">{{ selectedTalent.name }}</span></div>
|
||||
<div class="info-item"><span class="label">地区:</span><span class="value">{{ selectedTalent.location }}</span></div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item"><span class="label">性别:</span><span class="value">{{ selectedTalent.gender }}</span></div>
|
||||
<div class="info-item"><span class="label">研究领域:</span><span class="value">{{ selectedTalent.field }}</span></div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-item"><span class="label">年龄:</span><span class="value">{{ selectedTalent.age }}岁</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 个人介绍 -->
|
||||
<div class="biography-section">
|
||||
<h3>个人介绍</h3>
|
||||
<div class="biography-content">{{ selectedTalent.biography }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 人脉关系网 -->
|
||||
<div class="network-section">
|
||||
<div class="network-header">
|
||||
<h3>人脉关系网</h3>
|
||||
<div class="legend">
|
||||
<div class="legend-item"><div class="legend-line colleague"></div><span>合作关系</span></div>
|
||||
<div class="legend-item"><div class="legend-line friend"></div><span>朋友关系</span></div>
|
||||
<div class="legend-item"><div class="legend-line mentor"></div><span>师生关系</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="network-graph" class="network-container"></div>
|
||||
|
||||
<!-- Popover -->
|
||||
<el-popover
|
||||
v-if="selectedNode"
|
||||
:visible="!!selectedNode"
|
||||
placement="right"
|
||||
:width="220"
|
||||
trigger="manual"
|
||||
:virtual-ref="popoverTarget"
|
||||
virtual-triggering
|
||||
@hide="selectedNode = null"
|
||||
>
|
||||
<template #default>
|
||||
<div class="popover-content">
|
||||
<div class="popover-header"><h4>详细介绍</h4></div>
|
||||
<div class="popover-body">
|
||||
<div class="popover-item"><span class="popover-label">姓名:</span><span class="popover-value">{{ selectedNode.name }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">关系类型:</span><span class="popover-value">{{ getRelationText(selectedNode.relation) }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">性别:</span><span class="popover-value">{{ selectedNode.gender || '男' }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">年龄:</span><span class="popover-value">{{ selectedNode.age || '35' }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">工作单位:</span><span class="popover-value">{{ selectedNode.workplace || '某某公司' }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">职务:</span><span class="popover-value">{{ selectedNode.position || '高级工程师' }}</span></div>
|
||||
<div class="popover-item"><span class="popover-label">联系方式:</span><span class="popover-value">{{ selectedNode.contact || '***-****-****' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
|
||||
<div class="network-footer">
|
||||
<p>注:该关系网基于公开信息生成,仅供参考。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="no-selection">
|
||||
<el-empty description="请从左侧选择一个人才查看详情" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PageFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import * as d3 from 'd3'
|
||||
import PageNavigation from '@/components/PageNavigation.vue'
|
||||
import PageFooter from '@/components/PageFooter.vue'
|
||||
|
||||
interface TalentPerson {
|
||||
id: number
|
||||
name: string
|
||||
title: string
|
||||
field: string
|
||||
gender: string
|
||||
age: number
|
||||
location: string
|
||||
avatar?: string
|
||||
biography: string
|
||||
connections: Array<{
|
||||
id: number
|
||||
name: string
|
||||
relation: string
|
||||
strength: number
|
||||
gender?: string
|
||||
age?: number
|
||||
workplace?: string
|
||||
position?: string
|
||||
contact?: string
|
||||
}>
|
||||
}
|
||||
|
||||
interface NetworkNode {
|
||||
id: number
|
||||
name: string
|
||||
type: 'center' | 'connection'
|
||||
relation?: string
|
||||
strength?: number
|
||||
gender?: string
|
||||
age?: number
|
||||
workplace?: string
|
||||
position?: string
|
||||
contact?: string
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
|
||||
const selectedTalent = ref<TalentPerson | null>(null)
|
||||
const selectedNode = ref<NetworkNode | null>(null)
|
||||
const popoverTarget = ref<HTMLElement | null>(null)
|
||||
|
||||
const talentList = ref<TalentPerson[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: '周小然',
|
||||
title: '高级工程师',
|
||||
field: '人工智能',
|
||||
gender: '男',
|
||||
age: 28,
|
||||
location: '吉林省长春市长春理工大学',
|
||||
biography: '1994年毕业于中国人民解放军国防科技大学,1960年毕业于中国人民解放军国防科技大学,获得11月3日中国科学院,1961年5月,获得计算机硕士学位,现任为产品经理部副经理,请务必为产品经理部副经理工作上提供支持。1962年2月19日,开始从事软件开发工作,先后22年,获得软件开发证书等多项证书,请务必为产品经理部副经理工作上提供支持。1965年至今,少年时期在中国人民解放军国防科技大学,后进入中国科学院计算技术研究所,先后在多个科研院所工作,现任某某公司产品经理部副经理。1951年7月8日,少年时期在中国人民解放军国防科技大学,后进入中国科学院计算技术研究所,先后在多个科研院所工作,现任某某公司产品经理部副经理。1949年,教育背景为中国人民解放军国防科技大学计算机系,后进入中国科学院计算技术研究所,先后在多个科研院所工作,现任某某公司产品经理部副经理。1951年,在某某公司担任产品经理部副经理,负责产品规划和团队管理工作。10月,在某某公司担任产品经理部副经理,负责产品规划和团队管理工作。1951年11月,在某某公司担任产品经理部副经理,负责产品规划和团队管理工作。',
|
||||
connections: [
|
||||
{ id: 2, name: '王小轩', relation: 'colleague', strength: 0.8 },
|
||||
{ id: 3, name: '程小龙', relation: 'friend', strength: 0.6 },
|
||||
{ id: 4, name: '高小睿', relation: 'colleague', strength: 0.7 },
|
||||
{ id: 5, name: '张小祖', relation: 'mentor', strength: 0.9 },
|
||||
{ id: 6, name: '张小君', relation: 'colleague', strength: 0.5 }
|
||||
]
|
||||
},
|
||||
{ id: 2, name: '王小轩', title: '高级工程师', field: '人工智能', gender: '男', age: 32, location: '北京市海淀区', biography: '……', connections: [{ id: 1, name: '周小强', relation: 'colleague', strength: 0.8 }] },
|
||||
{ id: 3, name: '程小龙', title: '高级工程师', field: '生物技术', gender: '男', age: 29, location: '上海市浦东新区', biography: '……', connections: [{ id: 1, name: '周小强', relation: 'friend', strength: 0.6 }] },
|
||||
{ id: 4, name: '高小睿', title: '高级工程师', field: '计算机科学', gender: '男', age: 35, location: '深圳市南山区', biography: '……', connections: [{ id: 1, name: '周小强', relation: 'colleague', strength: 0.7 }] },
|
||||
{ id: 5, name: '张小祖', title: '高级工程师', field: '人工智能', gender: '男', age: 55, location: '北京市朝阳区', biography: '……', connections: [{ id: 1, name: '周小强', relation: 'mentor', strength: 0.9 }] },
|
||||
{ id: 6, name: '张小君', title: '高级工程师', field: '软件工程', gender: '女', age: 26, location: '杭州市西湖区', biography: '……', connections: [{ id: 1, name: '周小强', relation: 'colleague', strength: 0.5 }] }
|
||||
])
|
||||
|
||||
const selectTalent = (talent: TalentPerson) => {
|
||||
selectedTalent.value = talent
|
||||
selectedNode.value = null
|
||||
nextTick(renderNetworkGraph)
|
||||
}
|
||||
|
||||
const getRelationText = (relation: string) => {
|
||||
const map: Record<string, string> = { colleague: '合作关系', friend: '朋友关系', mentor: '师生关系' }
|
||||
return map[relation] || relation
|
||||
}
|
||||
const getRelationColor = (relation?: string) => ({ colleague: '#1890ff', friend: '#52c41a', mentor: '#fa8c16' }[relation || 'colleague']!)
|
||||
|
||||
const renderNetworkGraph = () => {
|
||||
if (!selectedTalent.value) return
|
||||
d3.select('#network-graph').selectAll('*').remove()
|
||||
|
||||
const width = 800, height = 500
|
||||
const svg = d3.select('#network-graph').append('svg').attr('width', width).attr('height', height).attr('viewBox', `0 0 ${width} ${height}`)
|
||||
|
||||
const center = { id: selectedTalent.value.id, name: selectedTalent.value.name, type: 'center' as const, x: width / 2, y: height / 2 }
|
||||
const conns = selectedTalent.value.connections.map((c, i) => {
|
||||
const angle = (i * 2 * Math.PI) / selectedTalent.value?.connections.length
|
||||
const r = 180
|
||||
return { id: c.id, name: c.name, type: 'connection' as const, relation: c.relation, strength: c.strength, x: width / 2 + Math.cos(angle) * r, y: height / 2 + Math.sin(angle) * r }
|
||||
})
|
||||
const nodes = [center, ...conns]
|
||||
const links = selectedTalent.value.connections.map((c, i) => ({ source: center, target: conns[i], relation: c.relation }))
|
||||
|
||||
// 三角形连线
|
||||
const linkGroup = svg.append('g').selectAll('g').data(links as any).enter().append('g')
|
||||
|
||||
linkGroup.each(function(d: any) {
|
||||
const group = d3.select(this)
|
||||
const x1 = d.source.x, y1 = d.source.y
|
||||
const x2 = d.target.x, y2 = d.target.y
|
||||
|
||||
// 计算连线的长度和角度
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
const length = Math.sqrt(dx * dx + dy * dy)
|
||||
const angle = Math.atan2(dy, dx)
|
||||
|
||||
// 三角形的数量和间距
|
||||
const triangleCount = Math.floor(length / 15) // 每15像素一个三角形
|
||||
|
||||
// 绘制三角形序列
|
||||
for (let i = 0; i < triangleCount; i++) {
|
||||
const progress = (i + 0.5) / triangleCount
|
||||
const x = x1 + dx * progress
|
||||
const y = y1 + dy * progress
|
||||
|
||||
// 三角形大小
|
||||
const size = 4
|
||||
|
||||
// 创建三角形路径
|
||||
const trianglePath = `M ${-size} ${-size/2} L ${size} 0 L ${-size} ${size/2} Z`
|
||||
|
||||
group.append('path')
|
||||
.attr('d', trianglePath)
|
||||
.attr('transform', `translate(${x}, ${y}) rotate(${angle * 180 / Math.PI})`)
|
||||
.attr('fill', getRelationColor(d.relation))
|
||||
.attr('opacity', 0.8)
|
||||
}
|
||||
})
|
||||
|
||||
const nodeGroup = svg.append('g').selectAll('g').data(nodes).enter().append('g')
|
||||
.attr('class', 'node-group').attr('transform', (d: any) => `translate(${d.x},${d.y})`)
|
||||
.style('cursor', 'pointer')
|
||||
.on('click', (event, d: any) => {
|
||||
if (d.type !== 'center') { popoverTarget.value = event.currentTarget as HTMLElement; selectedNode.value = { ...d } }
|
||||
})
|
||||
|
||||
// 尺寸
|
||||
const rContent = (d: any) => d.type === 'center' ? 45 : 30
|
||||
const bandH = (d: any) => d.type === 'center' ? 22 : 18 // 名牌高度(1/4~1/3圆的直径)
|
||||
const fontSize = (d: any) => d.type === 'center' ? 13 : 11
|
||||
|
||||
// 删除外层彩色圆圈,只保留内白圆和名牌
|
||||
|
||||
// 删除内白圆,直接显示头像
|
||||
|
||||
// 添加头像图片(铺满整个圆)
|
||||
nodeGroup.append('image')
|
||||
.attr('href', 'http://cdn.rayrayray.cn/avatar.png')
|
||||
.attr('x', (d: any) => -rContent(d))
|
||||
.attr('y', (d: any) => -rContent(d))
|
||||
.attr('width', (d: any) => rContent(d) * 2)
|
||||
.attr('height', (d: any) => rContent(d) * 2)
|
||||
.attr('clip-path', (d: any) => `url(#clip-avatar-${d.id})`)
|
||||
|
||||
// 为头像创建圆形裁剪路径
|
||||
const avatarDefs = nodeGroup.append('defs')
|
||||
avatarDefs.append('clipPath')
|
||||
.attr('id', (d: any) => `clip-avatar-${d.id}`)
|
||||
.append('circle')
|
||||
.attr('r', (d: any) => rContent(d))
|
||||
.attr('cx', 0).attr('cy', 0)
|
||||
|
||||
// === 关键:给每个节点创建 clipPath,用内白圆裁剪后续名牌 ===
|
||||
const defs = nodeGroup.append('defs')
|
||||
defs.append('clipPath')
|
||||
.attr('id', (d: any) => `clip-${d.id}`)
|
||||
.append('circle')
|
||||
.attr('r', (d: any) => rContent(d))
|
||||
.attr('cx', 0).attr('cy', 0)
|
||||
|
||||
// 名字底色(圆角矩形),被内圆裁剪 => 视觉等同于“overflow hidden”
|
||||
const labelWrap = nodeGroup.append('g').attr('clip-path', (d: any) => `url(#clip-${d.id})`)
|
||||
labelWrap.append('rect')
|
||||
.attr('width', (d: any) => 2 * rContent(d))
|
||||
.attr('height', (d: any) => bandH(d))
|
||||
.attr('x', (d: unknown) => -rContent(d))
|
||||
.attr('y', (d: unknown) => rContent(d) - bandH(d))
|
||||
.attr('fill', '#023d5e')
|
||||
.attr('opacity', 0.98)
|
||||
|
||||
// 文字(位于名牌中线)
|
||||
nodeGroup.append('text')
|
||||
.text((d: unknown) => d.name)
|
||||
.attr('font-size', (d: unknown) => `${fontSize(d)}px`)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dy', (d: unknown) => rContent(d) - bandH(d) / 2 + (d.type === 'center' ? 4 : 3))
|
||||
.attr('fill', '#fff')
|
||||
.attr('font-weight', 'bold')
|
||||
.style('paint-order', 'stroke')
|
||||
.style('stroke', 'rgba(0,0,0,0.22)')
|
||||
.style('stroke-width', '0.6px')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (talentList.value.length > 0) selectTalent(talentList.value[0])
|
||||
document.addEventListener('click', (e) => {
|
||||
const target = e.target as HTMLElement
|
||||
if (selectedNode.value && !target.closest('.el-popover') && !target.closest('.node-group')) selectedNode.value = null
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container{min-height:100vh;background:#f5f7fa;color:#303133;padding:80px 0 0;}
|
||||
.talent-layout{display:flex;max-width:1400px;margin:0 auto;gap:20px;padding:20px 40px;margin-top:20px;}
|
||||
.talent-sidebar{width:240px;background:#fff;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.1);height:fit-content;}
|
||||
.sidebar-header{padding:16px;border-bottom:1px solid #e4e7ed;}
|
||||
.sidebar-header h3{margin:0;font-size:16px;color:#303133;font-weight:600;}
|
||||
.talent-list{padding:8px 0;}
|
||||
.talent-item{padding:12px 16px;cursor:pointer;transition:all .2s;border-left:3px solid transparent;}
|
||||
.talent-item:hover{background:#f5f7fa;}
|
||||
.talent-item.active{background:#ecf5ff;border-left-color:#409eff;}
|
||||
.talent-name-main{font-size:14px;color:#303133;font-weight:600;margin-bottom:4px;}
|
||||
.talent-title-small{font-size:12px;color:#909399;}
|
||||
.talent-detail{flex:1;background:#fff;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,.1);padding:24px;}
|
||||
.detail-content{display:flex;flex-direction:column;gap:24px;}
|
||||
.basic-info-section{display:flex;gap:24px;padding-bottom:24px;border-bottom:1px solid #e4e7ed;}
|
||||
.detail-avatar{width:120px;height:120px;border-radius:8px;overflow:hidden;}
|
||||
.detail-avatar img{width:100%;height:100%;object-fit:cover;}
|
||||
.avatar-placeholder-large{width:100%;height:100%;background:#f0f0f0;display:flex;align-items:center;justify-content:center;font-size:48px;color:#c0c4cc;}
|
||||
.info-grid{flex:1;display:flex;flex-direction:column;gap:16px;}
|
||||
.info-row{display:flex;gap:40px;}
|
||||
.info-item{display:flex;align-items:center;gap:8px;min-width:200px;}
|
||||
.info-item .label{font-size:14px;color:#606266;min-width:80px;}
|
||||
.info-item .value{font-size:14px;color:#303133;font-weight:500;}
|
||||
.biography-section{padding-bottom:24px;border-bottom:1px solid #e4e7ed;}
|
||||
.biography-section h3{margin:0 0 16px;font-size:16px;color:#303133;font-weight:600;}
|
||||
.biography-content{font-size:14px;line-height:1.6;color:#606266;text-align:justify;}
|
||||
.network-section{display:flex;flex-direction:column;gap:16px;}
|
||||
.network-header{display:flex;justify-content:space-between;align-items:center;}
|
||||
.network-header h3{margin:0;font-size:16px;color:#303133;font-weight:600;}
|
||||
.legend{display:flex;gap:20px;}
|
||||
.legend-item{display:flex;align-items:center;gap:8px;font-size:12px;color:#606266;}
|
||||
.legend-line{width:20px;height:2px;}
|
||||
.legend-line.colleague{background:#1890ff;}
|
||||
.legend-line.friend{background:#52c41a;background-image:repeating-linear-gradient(90deg,#52c41a,#52c41a 6px,transparent 6px,transparent 9px);}
|
||||
.legend-line.mentor{background:#fa8c16;background-image:repeating-linear-gradient(90deg,#fa8c16,#fa8c16 9px,transparent 9px,transparent 13px);}
|
||||
.network-container{width:100%;height:500px;background:#fafafa;position:relative;overflow:visible;}
|
||||
.network-footer{text-align:center;}
|
||||
.network-footer p{margin:0;font-size:12px;color:#909399;}
|
||||
.popover-content{padding:0;}
|
||||
.popover-header{padding:12px 16px;border-bottom:1px solid #e4e7ed;background:#f8f9fa;margin:-12px -12px 0 -12px;}
|
||||
.popover-header h4{margin:0;font-size:14px;color:#303133;font-weight:600;}
|
||||
.popover-body{padding:12px 0;}
|
||||
.popover-item{display:flex;justify-content:space-between;align-items:center;padding:6px 0;border-bottom:1px solid #f0f0f0;}
|
||||
.popover-item:last-child{border-bottom:none;}
|
||||
.popover-label{font-size:12px;color:#606266;min-width:60px;}
|
||||
.popover-value{font-size:12px;color:#303133;font-weight:500;text-align:right;flex:1;}
|
||||
.no-selection{display:flex;align-items:center;justify-content:center;height:400px;}
|
||||
:deep(.node-group){transition:all .2s ease;}
|
||||
:deep(.node-group:hover circle){stroke-width:4px;}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
<template>
|
||||
<div class="page-container">
|
||||
<PageNavigation />
|
||||
|
||||
<div class="tech-resources-layout">
|
||||
<!-- 左侧分类导航 -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h3>科技资源</h3>
|
||||
</div>
|
||||
|
||||
<el-menu
|
||||
:default-active="activeCategory"
|
||||
class="category-menu"
|
||||
@select="handleCategorySelect"
|
||||
>
|
||||
<el-sub-menu index="papers">
|
||||
<template #title>
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>科技论文</span>
|
||||
</template>
|
||||
<el-menu-item index="papers-all">科技资讯</el-menu-item>
|
||||
<el-menu-item index="papers-ai">科技专利</el-menu-item>
|
||||
<el-menu-item index="papers-bio">科技政策</el-menu-item>
|
||||
<el-menu-item index="papers-cs">科技学者</el-menu-item>
|
||||
<el-menu-item index="papers-physics">科技院校</el-menu-item>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</div>
|
||||
|
||||
<!-- 右侧内容区域 -->
|
||||
<div class="content-area">
|
||||
<div class="content-header">
|
||||
<h2>科技论文</h2>
|
||||
<div class="search-controls">
|
||||
<el-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索"
|
||||
class="search-input"
|
||||
clearable
|
||||
>
|
||||
<template #suffix>
|
||||
<el-icon class="search-icon"><Search /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button type="primary">搜索</el-button>
|
||||
<el-button type="primary">导出</el-button>
|
||||
<el-button type="primary">批量下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 论文列表 -->
|
||||
<div class="papers-table">
|
||||
<el-table
|
||||
:data="papersList"
|
||||
style="width: 100%"
|
||||
:header-cell-style="{ background: '#f8f9fa', color: '#606266' }"
|
||||
>
|
||||
<el-table-column prop="title" label="题名" min-width="300" />
|
||||
<el-table-column prop="author" label="作者" width="150" />
|
||||
<el-table-column prop="journal" label="来源" width="120" />
|
||||
<el-table-column prop="date" label="发表时间" width="120" />
|
||||
<el-table-column prop="cited" label="被引" width="80" />
|
||||
<el-table-column prop="downloads" label="下载次数" width="100" />
|
||||
<el-table-column label="操作" width="80">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleView(scope.row)"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrapper">
|
||||
<div class="pagination-info">
|
||||
共 {{ total }} 条记录
|
||||
</div>
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="sizes, prev, pager, next"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<PageFooter />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Document, Search } from '@element-plus/icons-vue'
|
||||
import PageNavigation from '@/components/PageNavigation.vue'
|
||||
import PageFooter from '@/components/PageFooter.vue'
|
||||
|
||||
interface Paper {
|
||||
id: number
|
||||
title: string
|
||||
author: string
|
||||
journal: string
|
||||
date: string
|
||||
cited: number
|
||||
downloads: number
|
||||
}
|
||||
|
||||
const activeCategory = ref('papers-all')
|
||||
const searchKeyword = ref('')
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const total = ref(126)
|
||||
|
||||
const papersList = ref<Paper[]>([
|
||||
{
|
||||
id: 1,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: '基于深度学习的医疗诊断技术',
|
||||
author: '王小明, 黄小红',
|
||||
journal: '计算机工程',
|
||||
date: '2024/03/03',
|
||||
cited: 1587,
|
||||
downloads: 1579
|
||||
}
|
||||
])
|
||||
|
||||
const handleCategorySelect = (key: string) => {
|
||||
activeCategory.value = key
|
||||
// 这里可以根据分类加载不同的数据
|
||||
console.log('Selected category:', key)
|
||||
}
|
||||
|
||||
const handleView = (row: Paper) => {
|
||||
console.log('View paper:', row)
|
||||
// 这里可以跳转到论文详情页面
|
||||
}
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
pageSize.value = val
|
||||
// 重新加载数据
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val: number) => {
|
||||
currentPage.value = val
|
||||
// 重新加载数据
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 页面加载时的初始化操作
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.page-container {
|
||||
min-height: 100vh;
|
||||
background: #f5f7fa;
|
||||
padding: 80px 0 0;
|
||||
}
|
||||
|
||||
.tech-resources-layout {
|
||||
display: flex;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
gap: 20px;
|
||||
padding: 20px 40px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
/* 左侧边栏 */
|
||||
.sidebar {
|
||||
width: 240px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.sidebar-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.category-menu {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.category-menu .el-menu-item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.category-menu .el-sub-menu__title {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 右侧内容区域 */
|
||||
.content-area {
|
||||
flex: 1;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.content-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.search-controls {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 表格样式 */
|
||||
.papers-table {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.el-table th) {
|
||||
background: #f8f9fa !important;
|
||||
}
|
||||
|
||||
:deep(.el-table td) {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.el-table tr:hover > td) {
|
||||
background-color: #f8f9fa !important;
|
||||
}
|
||||
|
||||
/* 分页样式 */
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
:deep(.el-pagination) {
|
||||
--el-pagination-font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-pagination .btn-prev),
|
||||
:deep(.el-pagination .btn-next) {
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
:deep(.el-pagination .btn-prev:hover),
|
||||
:deep(.el-pagination .btn-next:hover) {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
:deep(.el-pager li.is-active) {
|
||||
background: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.tech-resources-layout {
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.search-controls {
|
||||
width: 100%;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pagination-wrapper {
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,314 @@
|
|||
<template>
|
||||
<div class="login-container">
|
||||
<!-- 背景装饰 -->
|
||||
<div class="background-decoration">
|
||||
<div class="decoration-circle circle-1"></div>
|
||||
<div class="decoration-circle circle-2"></div>
|
||||
<div class="decoration-circle circle-3"></div>
|
||||
<div class="decoration-lines">
|
||||
<div class="line line-1"></div>
|
||||
<div class="line line-2"></div>
|
||||
<div class="line line-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<div class="login-form-container">
|
||||
<div class="login-card">
|
||||
<div class="login-header">
|
||||
<h1>科技大脑·平台登录</h1>
|
||||
</div>
|
||||
|
||||
<el-form :model="loginForm" class="login-form" size="large">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
placeholder="请输入用户名"
|
||||
class="login-input"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><User /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
class="login-input"
|
||||
show-password
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Lock /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button
|
||||
@click="handleLogin"
|
||||
type="primary"
|
||||
class="login-button"
|
||||
:loading="isLoading"
|
||||
>
|
||||
登录
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<div class="login-footer">
|
||||
<span class="footer-text">没有账号?请联系管理员开通账号</span>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { User, Lock } from '@element-plus/icons-vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const loginForm = reactive({
|
||||
username: '',
|
||||
password: ''
|
||||
})
|
||||
|
||||
const isLoading = ref(false)
|
||||
|
||||
const handleLogin = async () => {
|
||||
if (!loginForm.username || !loginForm.password) {
|
||||
ElMessage.warning('请填写完整的登录信息')
|
||||
return
|
||||
}
|
||||
|
||||
isLoading.value = true
|
||||
|
||||
setTimeout(() => {
|
||||
isLoading.value = false
|
||||
ElMessage.success('登录成功!')
|
||||
router.push('/')
|
||||
}, 1500)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(135deg, #1e3a5f 0%, #2c5282 50%, #1a365d 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 背景装饰 */
|
||||
.background-decoration {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.decoration-circle {
|
||||
position: absolute;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.circle-1 {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
animation: float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.circle-2 {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
top: 60%;
|
||||
right: 15%;
|
||||
animation: float 8s ease-in-out infinite reverse;
|
||||
}
|
||||
|
||||
.circle-3 {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
bottom: 20%;
|
||||
left: 20%;
|
||||
animation: float 10s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.decoration-lines {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.line {
|
||||
position: absolute;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.line-1 {
|
||||
width: 300px;
|
||||
top: 25%;
|
||||
left: 5%;
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
|
||||
.line-2 {
|
||||
width: 200px;
|
||||
top: 70%;
|
||||
right: 10%;
|
||||
transform: rotate(-20deg);
|
||||
}
|
||||
|
||||
.line-3 {
|
||||
width: 250px;
|
||||
bottom: 15%;
|
||||
left: 15%;
|
||||
transform: rotate(10deg);
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0%, 100% {
|
||||
transform: translateY(0px);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 登录表单容器 */
|
||||
.login-form-container {
|
||||
z-index: 10;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: rgba(45, 82, 130, 0.9);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 16px;
|
||||
padding: 48px 40px;
|
||||
width: 400px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
color: #ffffff;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.login-input {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__wrapper) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 8px;
|
||||
box-shadow: none;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__wrapper:hover) {
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__wrapper.is-focus) {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__inner) {
|
||||
color: #ffffff;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__inner::placeholder) {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__prefix) {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
:deep(.login-input .el-input__suffix) {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #409eff 0%, #337ecc 100%);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-top: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.login-button:hover {
|
||||
background: linear-gradient(135deg, #337ecc 0%, #2b6cb0 100%);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
text-align: center;
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.login-card {
|
||||
width: 90%;
|
||||
max-width: 360px;
|
||||
padding: 32px 24px;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.decoration-circle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.decoration-lines {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": false,
|
||||
"jsx": "preserve",
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"useDefineForClassFields": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"types": [
|
||||
"webpack-env",
|
||||
"node"
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"scripthost"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.vue",
|
||||
"tests/**/*.ts",
|
||||
"tests/**/*.tsx"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true
|
||||
transpileDependencies: true,
|
||||
publicPath: './'
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue