sd 4 months ago
parent
commit
e8e9e4d4b3
  1. 65
      vue/src/App.vue
  2. 263
      vue/src/components/Menu.vue
  3. 54
      vue/src/system/Index.vue
  4. 483
      vue/src/system/Login.vue

65
vue/src/App.vue

@ -1,69 +1,10 @@
<template>
<!-- 控制面板可选 -->
<!-- <div class="controls">-->
<!-- <button @click="toggleLabel">切换节点标签</button>-->
<!-- </div>-->
<!-- 知识图谱 -->
<GraphDemo
:graph-data="knowledgeData"
:node-style="nodeConfig"
:edge-style="edgeConfig"
ref="graphRef"
/>
<router-view />
</template>
<script>
import GraphDemo from './system/GraphDemo.vue';
export default {
name: 'App',
components: {
GraphDemo
},
data() {
return {
knowledgeData: {
nodes: [
{ id: 'entity1', label: '人工智能', style: { fill: '#FFD700' } },
{ id: 'entity2', label: '机器学习', style: { shape: 'rect', size: 80 } },
{ id: 'entity3', label: '深度学习' }
],
edges: [
{ source: 'entity1', target: 'entity2', label: '包含' },
{ source: 'entity2', target: 'entity3', label: '子领域' }
]
},
nodeConfig: {
shape: 'circle',
size: 70,
fill: '#9FD5FF',
stroke: '#5B8FF9',
lineWidth: 2,
showLabel: true,
labelFontFamily: 'Microsoft YaHei',
labelFontSize: 16,
labelColor: '#333'
},
edgeConfig: {
type: 'quadratic', // 线
stroke: '#666',
lineWidth: 2,
endArrow: true,
showLabel: true,
labelFontFamily: 'Microsoft YaHei',
labelFontSize: 14,
labelColor: '#888'
}
}
},
methods:{
toggleLabel() {
this.nodeConfig.showLabel = !this.nodeConfig.showLabel
}
}
name: 'App'
}
</script>
@ -75,4 +16,4 @@ export default {
text-align: center;
color: #2c3e50;
}
</style>
</style>

263
vue/src/components/Menu.vue

@ -0,0 +1,263 @@
<template>
<div class="sidebar-container">
<!-- 系统标题区域 -->
<div class="sidebar-header">
<h1 class="sidebar-title">
面向疾病预测的知识图谱应用系统
</h1>
</div>
<!-- 菜单列表 -->
<nav class="sidebar-nav">
<ul>
<li
v-for="(item, index) in menuItems"
:key="index"
class="menu-item"
>
<a
@click.prevent="handleMenuClick(index)"
class="menu-link"
:class="{ 'active': activeIndex === index }"
>
<span class="menu-icon">{{ item.icon }}</span>
<span>{{ item.name }}</span>
</a>
</li>
</ul>
</nav>
<!-- 底部用户信息和退出登录区域 -->
<div class="sidebar-footer">
<div class="user-info">
<div class="user-avatar">
<span class="avatar-text"></span>
</div>
<div class="user-details">
<p class="user-name">管理员</p>
<p class="user-email">admin@example.com</p>
</div>
<button @click="handleLogout" class="logout-btn-inline">
<span class="logout-icon">🚪</span>
</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, defineProps, defineEmits } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
//
const props = defineProps({
//
initialActive: {
type: Number,
default: 0
}
});
//
const emit = defineEmits(['menu-click']);
//
const menuItems = ref([
{
name: '医疗知识图谱',
path: '/medical-kg',
icon: '🏥'
},
{
name: '知识图谱构建',
path: '/kg-construction',
icon: '🔧'
},
{
name: '知识图谱问答',
path: '/kg-qa',
icon: '💬'
},
{
name: '知识图谱数据',
path: '/kg-data',
icon: '📊'
}
]);
//
const activeIndex = ref(props.initialActive);
//
const handleMenuClick = (index) => {
activeIndex.value = index;
emit('menu-click', menuItems.value[index]);
};
// 退
const handleLogout = () => {
// 使Vue Router
router.push('/login');
};
</script>
<style scoped>
.sidebar-container {
width: 240px;
height: 100vh;
background-color: #1e40af;
color: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 1.25rem;
border-bottom: 1px solid #1e3a8a;
}
.sidebar-title {
font-size: 1.25rem;
font-weight: bold;
letter-spacing: -0.025em;
}
.sidebar-nav {
flex: 1;
overflow-y: auto;
padding: 1rem 0;
}
.sidebar-nav ul {
padding-left: 0;
margin: 0;
}
.menu-item {
margin-bottom: 0.25rem;
list-style-type: none;
}
.menu-link {
display: flex;
align-items: center;
padding: 0.75rem 1.25rem;
color: white;
text-decoration: none;
transition: background-color 0.2s;
font-size: 1.1rem;
}
.menu-link:hover {
background-color: #1e3a8a;
}
.menu-link.active {
background-color: #1e3a8a;
border-left: 4px solid #60a5fa;
}
.menu-icon {
margin-right: 0.75rem;
color: #93c5fd;
font-size: 1.4rem;
}
.menu-link:hover .menu-icon {
color: white;
}
.sidebar-footer {
padding: 1rem;
border-top: 1px solid #1e3a8a;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar {
width: 2rem;
height: 2rem;
border-radius: 50%;
background-color: #274eb8;
display: flex;
align-items: center;
justify-content: center;
margin-right: 0.75rem;
}
.avatar-text {
font-size: 0.875rem;
}
.user-details {
flex: 1;
}
.user-name {
font-size: 0.875rem;
font-weight: 500;
margin: 0;
}
.user-email {
font-size: 0.75rem;
color: #93c5fd;
margin: 0;
}
/* 退出登录按钮样式 */
.logout-btn-inline {
background: transparent;
border: none;
color: #93c5fd;
cursor: pointer;
padding: 0.5rem;
border-radius: 50%;
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.logout-btn-inline:hover {
background-color: #1e3a8a;
color: white;
}
.logout-icon {
font-size: 1.2rem;
}
/* 响应式调整 */
@media (max-width: 768px) {
.sidebar-container {
width: 70px;
}
.sidebar-title,
.menu-link span:not(.menu-icon),
.user-details {
display: none;
}
.menu-link {
justify-content: center;
padding: 0.5rem;
}
.menu-icon {
margin-right: 0;
font-size: 1.2rem;
}
.menu-link.active {
border-left-width: 2px;
}
}
</style>

54
vue/src/system/Index.vue

@ -0,0 +1,54 @@
<template>
<div class="app-container">
<!-- 引入侧边栏组件 -->
<Menu
:initial-active="0"
@menu-click="handleSidebarClick"
/>
<!-- 主内容区域 -->
<div class="main-content">
<h1 class="text-2xl font-bold mb-4">首页</h1>
<div class="bg-white p-6 rounded-lg shadow">
<p>欢迎使用面向疾病预测的知识图谱应用系统</p>
</div>
</div>
</div>
</template>
<script setup>
import Menu from '../components/Menu.vue';
//
const handleSidebarClick = (menuItem) => {
console.log('点击了菜单项:', menuItem);
//
};
</script>
<style scoped>
.app-container {
display: flex;
height: 100vh;
overflow: hidden;
}
.main-content {
flex: 1;
padding: 1.5rem;
overflow-y: auto;
height: 100%;
}
/* 确保左侧导航栏完全固定 */
.app-container > :first-child {
position: fixed;
height: 100vh;
z-index: 10;
}
/* 为右侧内容添加左边距,避免被固定导航栏遮挡 */
.main-content {
margin-left: 240px; /* 与Menu.vue中的sidebar-container宽度相同 */
}
</style>

483
vue/src/system/Login.vue

@ -0,0 +1,483 @@
<template>
<div class="login-container">
<!-- 左上角Logo和标题 -->
<div class="logo-header">
<img src="@/assets/logo.png" alt="Logo" class="logo">
<h1 class="login-title">面向疾病预测的知识图谱应用系统</h1>
</div>
<!-- 左侧登录区域 -->
<div class="login-form-container">
<div class="login-header">
</div>
<div class="login-form">
<h2 class="form-title">登录</h2>
<p class="form-description">请输入您的电子邮件地址和密码以访问账户</p>
<form class="form" @submit.prevent="handleLogin">
<div class="form-group">
<label for="username" class="form-label">用户名</label>
<input
type="text"
id="username"
v-model="loginForm.username"
placeholder="输入您的用户名"
class="form-input"
>
</div>
<div class="form-group">
<div class="password-header">
<label for="password" class="form-label">密码</label>
<a href="#" class="forgot-password">忘记密码?</a>
</div>
<input
type="password"
id="password"
v-model="loginForm.password"
placeholder="输入您的密码"
class="form-input"
>
</div>
<div class="form-checkbox">
<input
type="checkbox"
id="remember"
v-model="loginForm.remember"
class="checkbox"
>
<label for="remember" class="checkbox-label">记住密码</label>
</div>
<button
type="submit"
class="login-button"
>
<img src="@/assets/登录.png" alt="登录" class="login-icon">
<span>登录</span>
</button>
</form>
<div class="social-login">
<p class="social-text">使用其他方式登录</p>
</div>
<div class="register-link">
<p>还没有账户? <a href="#" class="register"> 立即注册</a></p>
</div>
</div>
</div>
<!-- 右侧知识图谱可视化区域 -->
<div class="graph-container">
<!-- 背景装饰 -->
<div class="background-decoration">
<div class="bg-circle circle-1"></div>
<div class="bg-circle circle-2"></div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
//
const loginForm = ref({
username: '',
password: '',
remember: false
});
//
const handleLogin = () => {
console.log('登录信息:', loginForm.value);
//
// index
router.push('/index');
};
</script>
<style>
/* 全局样式,防止页面滚动 */
body, html {
margin: 0;
padding: 0;
overflow: hidden;
height: 100%;
}
</style>
<style scoped>
/* 基础容器样式 */
.login-container {
display: flex;
height: 100vh;
overflow: hidden;
flex-direction: row;
font-family: 'SimSun', '宋体', serif;
}
/* 左上角Logo和标题样式 */
.logo-header {
position: fixed;
top: 40px;
left: 25px;
display: flex;
align-items: center;
z-index: 10;
}
.logo {
height: 15px;
width: 15px;
margin-right: 7px;
}
.login-title {
font-size: 17px;
font-weight: 900;
font-family: 'SimSun Bold', '宋体', serif;
color: #1f2937;
margin: 0;
white-space: nowrap;
text-shadow: 0.4px 0.4px 0 #1f2937;
}
/* 左侧登录区域样式 */
.login-form-container {
width: 25%;
background-color: #ffffff;
padding: 2rem;
padding-left: 40px;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.login-header {
margin-bottom: 1.5rem;
width: 100%;
max-width: 24rem;
margin-top: 20px;
}
.login-form {
max-width: 24rem;
width: 100%;
text-align: left;
}
.form-title {
font-size: 18px;
font-weight: 900;
color: #333333;
margin-top: -7px;
margin-bottom: 10px;
margin-left: 13px;
text-shadow: 0.2px 0.2px 0 #1f2937;
text-align: left;
font-family: 'SimSun', '宋体', serif;
}
.form-description {
color: #B5B5B5;
margin-bottom: 2rem;
margin-left: 13px;
text-align: left;
font-size: 11px;
font-weight: bold;
font-family: 'SimSun', '宋体', serif;
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
width: 100%;
}
.form-group {
display: flex;
flex-direction: column;
width: 100%;
margin-bottom: 0.5rem;
}
.form-label {
display: block;
font-size: 11px;
font-weight: 700;
color: #374151;
margin-bottom: 0.3rem;
text-align: left;
font-family:'STSong', '宋体', serif;
}
.form-input {
width: 100%;
padding: 0.6rem 0.8rem;
border-radius: 0.5rem;
border: 2px solid #A3A3A3;
transition: all 0.2s;
font-size: 9px;
box-sizing: border-box;
font-family: 'SimSun', '宋体', serif;
background-color: #FFFFFF;
}
.form-input:focus {
outline: none;
border-color: #2563eb;
box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}
.form-input::placeholder {
color: #9ca3af;
}
.password-header {
display: flex;
justify-content: space-between;
margin-bottom: 0.1rem;
}
.forgot-password {
font-size: 9px;
color: #B5B5B5;
text-decoration: none;
font-family: 'SimSun', '宋体', serif;
}
.forgot-password:hover {
color: #1d4ed8;
}
.form-checkbox {
display: flex;
align-items: center;
margin-top: -5px;
margin-bottom: -5px;
}
.checkbox {
height: 0.8rem;
width: 0.8rem;
color: #2563eb;
border-radius: 0.25rem;
border: 1px solid #d1d5db;
}
.checkbox-label {
margin-left: 0.1rem;
font-size: 11px;
color: #444040ba;
font-weight: bold;
font-family: 'SimSun', '宋体', serif;
}
.login-button {
width: 100%;
background-color: #409EFF;
color: white;
font-weight: 500;
font-size: 11px;
padding: 0.6rem 0.8rem;
border-radius: 0;
border: none;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
font-family: 'SimSun', '宋体', 'STSong', '华文宋体', serif;
box-shadow: 3px 3px 5px rgba(0, 0, 0, 0.2);
}
.login-icon {
height: 0.9rem;
width: auto;
margin-right: 8px;
}
.login-button:hover {
background-color: #1d4ed8;
}
.arrow-icon {
margin-left: 0.5rem;
}
/* 分割线样式 */
.divider {
display: flex;
align-items: center;
margin: 1.5rem 0;
font-family: 'SimSun', '宋体', serif;
}
.divider::before,
.divider::after {
content: '';
flex: 1;
height: 1px;
background-color: #e5e7eb;
}
.divider span {
padding: 0 1rem;
font-size: 11px;
color: #B5B5B5;
font-family: 'SimSun', '宋体', serif;
}
.social-login {
margin-top: 2rem;
}
.social-text {
text-align: center;
color: #B5B5B5;
margin-bottom: 1rem;
font-size: 11px;
font-weight: bold;
font-family: 'SimSun', '宋体', serif;
}
.social-icons {
display: flex;
justify-content: center;
gap: 1rem;
}
.social-icon {
width: 2.5rem;
height: 2.5rem;
border-radius: 50%;
background-color: #f3f4f6;
border: none;
cursor: pointer;
transition: background-color 0.2s;
display: flex;
align-items: center;
justify-content: center;
color: #6b7280;
font-weight: bold;
}
.social-icon:hover {
background-color: #e5e7eb;
}
.register-link {
position: absolute;
bottom: 7px;
left: 50%;
transform: translateX(-50%);
text-align: center;
}
.register-link p {
color: #B5B5B5;
font-size: 11px;
font-weight: bold;
font-family: 'SimSun', '宋体', serif;
}
.register {
color: #B5B5B5;
font-weight: 500;
text-decoration: none;
font-weight: bold;
font-family: 'SimSun', '宋体', serif;
}
.register:hover {
color: #1d4ed8;
}
/* 右侧知识图谱可视化区域样式 */
.graph-container {
width: 75%;
background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 100%),
url('@/assets/背景.png');
background-size: cover;
background-position: center;
background-blend-mode: overlay;
padding: 2rem;
position: relative;
overflow: hidden;
}
.background-decoration {
position: absolute;
inset: 0;
opacity: 0.2;
}
.bg-circle {
position: absolute;
border-radius: 50%;
filter: blur(3rem);
}
.circle-1 {
top: 25%;
left: 25%;
width: 16rem;
height: 16rem;
background-color: #60a5fa;
}
.circle-2 {
bottom: 33%;
right: 33%;
width: 20rem;
height: 20rem;
background-color: #818cf8;
}
.graph-content {
position: relative;
z-index: 10;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
font-family: 'SimSun', '宋体', serif;
}
.graph-wrapper {
position: relative;
width: 100%;
max-width: 48rem;
aspect-ratio: 1 / 1;
}
/* 响应式设计 */
@media (max-width: 768px) {
.login-container {
flex-direction: column;
}
.login-form-container,
.graph-container {
width: 100%;
}
.login-form-container {
padding: 2rem;
}
.graph-container {
min-height: 400px;
}
}
</style>
Loading…
Cancel
Save