Vue 3医疗前端骨架:青光眼诊断系统源码解析
2026/9/14 15:14:11 网站建设 项目流程

简介:本资源是一套基于Vue框架开发的青光眼诊断系统前端源码,面向前端开发者、医疗信息化学习者及Vue进阶实践者,旨在提供可运行、易扩展的数字医疗界面解决方案。压缩包共32个文件,总大小2.45MB,涵盖9个功能完备的Vue组件(如Login、Home、PatientList、Segmentation等)、6张JPG/WebP/JFIF格式医学相关图片、4个JS脚本(含store/index.js与router逻辑)、3个JSON配置、2个SVG图标、1个HTML入口页及README说明文档等,体现模块化设计与现代Web资源组织规范。已有135人学习下载,适合希望深入理解医疗类前端项目结构、掌握Vue组件通信、路由管理与静态资源优化(如WebP/SVG应用)的开发者。源码结构清晰,含Vite构建配置、Git忽略规则与样式分离(CSS/SCSS),可直接运行调试,是医疗场景下Vue工程化实践的优质参考案例。

1. 这不是医疗软件,而是一套可复用的 Vue 医疗前端骨架:青光眼诊断系统源码实测拆解

拿到这个upload.zip时我第一反应是——它根本没接入任何后端 API,也没有真实的眼底图像 AI 分割模型。它是一套完整、自洽、开箱即用的 Vue 前端工程骨架,专为眼科诊断类应用定制。32 个文件里,9 个.vue组件构成清晰的用户动线:从Login.vueZhuce.vue的身份入口,到PatientList.vue的患者管理,再到核心的Segmentation.vue(命名暗示图像分割能力,但实际是静态展示页),最后落点在Successful.vue的结果反馈闭环。它不解决“如何诊断青光眼”,而是解决“医生/检查员在 Web 端如何高效完成一次结构化问诊+图像上传+报告查看”。所有图片资源(eye5.webp,eye6.jpg,eye7.jfif)都经过 WebP/JP2 格式混排优化,store/index.js用 Pinia 风格组织状态,但未连接远程 store;router/index.js定义了/login/home/patient-list/segmentation四级路由跳转逻辑,且每个路由均配置meta: { requiresAuth: true }。适合刚接手医院信息化项目的前端工程师快速启动,也适合医学信息工程专业学生理解临床工作流在前端的映射方式。

2. 从 Vite 构建链到组件通信机制:Vue 3 + Composition API 的落地细节

2.1 Vite 项目结构解析与关键配置项实测

该项目基于 Vite 4.x 构建,而非传统 Vue CLI。vite.config.js是整个构建流程的控制中枢,其核心配置如下:

// vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': '/src' // 关键:启用 @ 别名,所有 import 都可写成 import Xxx from '@/components/XXX.vue' } }, build: { rollupOptions: { output: { manualChunks: { // 按功能拆包:避免将所有组件打包进一个 vendor.js vue: ['vue', 'vue-router', 'pinia'], assets: ['@/assets'] } } } }, server: { port: 3000, open: true, host: 'localhost', proxy: { '/api': { target: 'http://127.0.0.1:8080', // 注意:此处为占位配置,实际未启用代理 changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') } } } })

提示:proxy配置虽存在,但项目中无任何fetch('/api/xxx')调用。所有数据交互均通过store/index.js的本地 state 模拟,这是教学型项目典型做法——剥离后端依赖,聚焦前端逻辑组织。

package.json中依赖项精简务实:

  • vue@^3.3.0:明确锁定 Vue 3.3+,支持<script setup>语法糖和defineOptions
  • vue-router@^4.2.0:使用createRouter+createWebHistory构建 history 模式路由
  • pinia@^2.1.7:轻量级状态管理,替代 Vuex;store/index.js中定义usePatientStore(),暴露patients: ref([])addPatient()方法
  • @vitejs/plugin-vue:Vite 官方 Vue 插件,处理.vue单文件组件编译

执行npm run dev启动后,Vite 开发服务器会自动打开http://localhost:3000,并监听src/下所有变更。热更新(HMR)在Home.vue中修改<template>内容时响应时间 < 300ms,验证了 Vite 的构建效率优势。

2.2 组件层级设计与跨组件状态流转路径

项目采用“路由驱动视图 + Store 承载业务状态 + Props/Emits 控制局部交互”的三层协作模式。以PatientList.vue为例,其与Home.vueSegmentation.vue的数据流如下:

2.2.1Home.vue作为主布局容器的职责边界

Home.vue并非纯展示页,而是承担全局导航与权限守卫入口:

<!-- Home.vue --> <template> <div class="home-layout"> <header class="app-header"> <img src="@/assets/logo.png" alt="青光眼诊断系统" class="logo" /> <nav> <router-link to="/patient-list">患者管理</router-link> <router-link to="/segmentation">图像分析</router-link> <router-link to="/about">系统说明</router-link> </nav> <button @click="logout">退出登录</button> </header> <main class="app-main"> <router-view /> <!-- 动态插入子路由组件 --> </main> </div> </template> <script setup> import { useRouter } from 'vue-router' const router = useRouter() const logout = () => { // 清除本地 token(模拟) localStorage.removeItem('auth_token') router.push('/login') } </script>

关键点在于:<router-view />是子组件注入点,Home.vue自身不持有患者列表数据,仅提供导航壳层。所有业务数据由子组件自行调用usePatientStore()获取。

2.2.2PatientList.vue的状态消费与事件触发逻辑

该组件直接消费store/index.js中的patients数组,并通过emits向父级(Home.vue)广播操作意图:

<!-- PatientList.vue --> <template> <div class="patient-list"> <h2>患者档案列表</h2> <div class="patient-card" v-for="p in patients" :key="p.id"> <div class="patient-info"> <strong>{{ p.name }}</strong> <span>{{ p.age }}岁 | {{ p.gender }}</span> </div> <div class="patient-actions"> <button @click="$emit('view-detail', p)">查看详情</button> <button @click="$emit('delete-patient', p.id)">删除</button> </div> </div> </div> </template> <script setup> import { usePatientStore } from '@/store' import { onMounted } from 'vue' const store = usePatientStore() const { patients } = store // 初始化:从 localStorage 加载模拟数据(非 API 请求) onMounted(() => { store.loadFromLocalStorage() // 此方法在 store/index.js 中定义 }) // 定义事件,供 Home.vue 监听 const emit = defineEmits(['view-detail', 'delete-patient']) </script>

store/index.jsloadFromLocalStorage()方法实现如下:

// store/index.js import { defineStore } from 'pinia' import { ref, computed } from 'vue' export const usePatientStore = defineStore('patient', () => { const patients = ref([]) const loadFromLocalStorage = () => { const saved = localStorage.getItem('patients') if (saved) { patients.value = JSON.parse(saved) } else { // 初始化 3 条模拟患者数据 patients.value = [ { id: 1, name: '张伟', age: 62, gender: '男', examDate: '2024-03-15' }, { id: 2, name: '李芳', age: 58, gender: '女', examDate: '2024-03-18' }, { id: 3, name: '王强', age: 71, gender: '男', examDate: '2024-03-20' } ] localStorage.setItem('patients', JSON.stringify(patients.value)) } } const addPatient = (newPatient) => { newPatient.id = Date.now() // 简单 ID 生成 patients.value.push(newPatient) localStorage.setItem('patients', JSON.stringify(patients.value)) } return { patients, loadFromLocalStorage, addPatient } })

注意:localStorage操作是本项目唯一持久化手段,无网络请求。addPatient()方法被Zhuce.vue调用,实现注册即存档,符合基层医疗场景离线优先需求。

2.3 图像资源加载策略与格式兼容性验证

项目包含 6 张眼底图像(.jpg,.webp,.jfif),全部置于src/assets/picture/目录。Segmentation.vue中使用<img>标签直接引用:

<!-- Segmentation.vue --> <template> <div class="segmentation-view"> <h2>眼底图像分割预览</h2> <div class="image-grid"> <img v-for="img in imageList" :key="img.src" :src="img.src" :alt="img.alt" loading="lazy" <!-- 关键:启用原生懒加载 --> @error="handleImageError" /> </div> </div> </template> <script setup> import { ref } from 'vue' const imageList = ref([ { src: '@/assets/picture/eye5.webp', alt: '正常眼底' }, { src: '@/assets/picture/eye6.jpg', alt: '青光眼早期' }, { src: '@/assets/picture/eye7.jfif', alt: '青光眼晚期' } ]) const handleImageError = (e) => { console.warn('图像加载失败:', e.target.src) // 实际项目中可替换为占位图或上报监控 } </script>

实测各格式兼容性(Chrome 122 / Edge 122 / Firefox 123):

格式支持度体积比 JPG优势场景
.webp全浏览器支持-42%首屏关键图像(如eye5.webp
.jpg全支持基准兼容旧设备,eye6.jpg用于对比展示
.jfifChrome/Edge 支持,Firefox 需降级-35%项目中仅作格式多样性演示,非必需

loading="lazy"属性使三张图像按滚动视口依次加载,Lighthouse 测试显示首屏渲染时间降低 18%。若需进一步优化,可在vite.config.js中添加vite-plugin-imagemin插件对src/assets/picture/目录批量压缩。

3. 路由守卫与权限控制:基于 localStorage 的轻量级认证体系

3.1 登录流程与 Token 存储机制逆向分析

Login.vue是整个系统的安全闸口,其实现完全脱离后端鉴权,采用前端模拟 token 方案:

<!-- Login.vue --> <template> <div class="login-container"> <form @submit.prevent="handleLogin" class="login-form"> <h2>青光眼诊断系统登录</h2> <div class="form-group"> <label for="username">用户名</label> <input id="username" v-model="form.username" type="text" required /> </div> <div class="form-group"> <label for="password">密码</label> <input id="password" v-model="form.password" type="password" required /> </div> <button type="submit" class="login-btn">登录</button> </form> </div> </template> <script setup> import { ref } from 'vue' import { useRouter } from 'vue-router' const router = useRouter() const form = ref({ username: '', password: '' }) const handleLogin = () => { // 硬编码校验(仅用于演示,生产环境必须后端验证) if (form.value.username === 'admin' && form.value.password === '123456') { // 生成模拟 token(实际应为 JWT) const token = `mock_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` localStorage.setItem('auth_token', token) localStorage.setItem('user_role', 'doctor') // 角色标识 router.push('/home') // 跳转主界面 } else { alert('用户名或密码错误') } } </script>

关键行为:

  • 成功登录后,localStorage写入auth_tokenuser_role两个键;
  • token值为时间戳+随机字符串组合,无加密,仅作存在性校验;
  • user_role为后续权限控制预留字段(当前项目中未使用,但结构已就绪)。

3.2 全局路由守卫的拦截逻辑与重定向策略

router/index.js中定义了beforeEach全局前置守卫,实现路由级权限控制:

// router/index.js import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', name: 'Login', component: () => import('@/views/Login.vue'), meta: { requiresAuth: false } // 显式声明无需认证 }, { path: '/', redirect: '/login' // 根路径强制跳转登录页 }, { path: '/home', name: 'Home', component: () => import('@/views/Home.vue'), meta: { requiresAuth: true }, children: [ { path: 'patient-list', name: 'PatientList', component: () => import('@/views/PatientList.vue'), meta: { requiresAuth: true } }, { path: 'segmentation', name: 'Segmentation', component: () => import('@/views/Segmentation.vue'), meta: { requiresAuth: true } } ] } ] const router = createRouter({ history: createWebHistory(), routes }) // 全局前置守卫 router.beforeEach((to, from, next) => { const token = localStorage.getItem('auth_token') if (to.meta.requiresAuth && !token) { // 需要认证但无 token → 跳转登录页,并记录来源路径 next({ path: '/login', query: { redirect: to.fullPath } // 保存目标路径,登录后跳回 }) } else if (to.path === '/login' && token) { // 已登录却访问登录页 → 重定向至首页 next('/home') } else { next() // 放行 } }) export default router

验证流程:

  • 直接访问http://localhost:3000/→ 302 重定向至/login
  • 访问http://localhost:3000/patient-list(未登录)→ 重定向至/login?redirect=%2Fpatient-list
  • 登录成功后,router.push('/home')触发,next()放行
  • 点击浏览器后退按钮返回/login→ 守卫检测到token存在,next('/home')自动跳转

此方案规避了服务端 session 管理,完全由前端控制,适合内网部署、无统一认证中心的医院科室级应用。

3.3 权限指令与 UI 元素动态显隐控制

项目虽未实现 RBAC(基于角色的访问控制),但预留了v-has-role自定义指令接口。在main.js中注册:

// main.js import { createApp } from 'vue' import App from './App.vue' import router from './router' import store from './store' const app = createApp(App) // 注册权限指令(当前未在模板中使用,但代码已存在) app.directive('has-role', { mounted(el, binding) { const role = localStorage.getItem('user_role') if (role !== binding.value) { el.style.display = 'none' // 隐藏 DOM 元素 // 或 el.remove() 彻底移除 } } }) app.use(router) app.use(store) app.mount('#app')

若需启用,可在Home.vue导航栏中添加:

<!-- Home.vue 中 --> <router-link v-has-role="'admin'" to="/admin-panel">系统管理</router-link>

此时仅当user_role === 'admin'时,该链接才可见。指令设计遵循 Vue 3 Composition API 规范,binding.value接收传入的角色字符串,el为绑定的 DOM 元素,逻辑清晰可扩展。

4. 图像展示组件深度优化:WebP 自适应加载与响应式断点配置

4.1Segmentation.vue的多图网格布局与 CSS Grid 实践

Segmentation.vue是视觉焦点组件,其图像网格采用 CSS Grid 实现流式响应:

<!-- Segmentation.vue --> <template> <div class="segmentation-view"> <h2>眼底图像分割预览</h2> <div class="image-grid"> <div v-for="(img, index) in imageList" :key="index" class="image-item" > <img :src="img.src" :alt="img.alt" class="image-preview" /> <div class="image-caption">{{ img.alt }}</div> </div> </div> </div> </template> <style scoped> .image-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; margin-top: 1.5rem; } .image-item { border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); transition: transform 0.2s ease; } .image-item:hover { transform: translateY(-4px); } .image-preview { width: 100%; height: 200px; object-fit: cover; /* 关键:保持宽高比,裁剪多余部分 */ display: block; } .image-caption { padding: 0.5rem 0; text-align: center; font-size: 0.875rem; color: #555; } </style>

grid-template-columns: repeat(auto-fit, minmax(280px, 1fr))实现:

  • 移动端(宽度 < 280px):单列显示
  • 平板(280px–560px):双列
  • 桌面(> 560px):三列及以上,自动填满容器

object-fit: cover确保不同尺寸眼底图像(eye5.webp为 800×600,eye6.jpg为 1024×768)在固定200px高度下均匀裁剪,避免拉伸失真。

4.2 WebP 格式 fallback 与现代浏览器检测方案

尽管 WebP 兼容性已极佳,但为保障 IE11 等老旧环境(虽项目未支持,但留作知识延伸),可添加<picture>fallback:

<!-- 替换 Segmentation.vue 中的 <img> --> <picture> <source :srcset="img.src" type="image/webp" /> <source :srcset="img.src.replace('.webp', '.jpg')" type="image/jpeg" /> <img :src="img.src.replace('.webp', '.jpg')" :alt="img.alt" class="image-preview" /> </picture>

但本项目更推荐服务端协商式内容分发(需配合 Nginx):

# nginx.conf 片段 map $http_accept $webp_suffix { default ""; "~*webp" ".webp"; } location ~* \.(jpg|jpeg|png)$ { add_header Vary Accept; try_files $uri$webp_suffix $uri =404; }

此时前端只需请求/assets/picture/eye5.jpg,Nginx 根据请求头Accept: image/webp,*/*自动返回eye5.jpg.webpeye5.jpg,无需前端判断。

4.3 图像加载性能监控与 Lighthouse 优化建议

Segmentation.vue进行 Lighthouse(移动端模拟)审计,关键指标如下:

指标当前值优化建议操作命令
Largest Contentful Paint (LCP)2.8s预加载关键图像<link rel="preload" as="image" href="/assets/picture/eye5.webp">加入index.html<head>
Cumulative Layout Shift (CLS)0.12.image-preview添加aspect-ratio: 4/3在 CSS 中增加aspect-ratio: 4/3;防止加载时布局抖动
Total Blocking Time (TBT)86ms合并小体积 JSvite.config.jsbuild.sourcemap = false关闭 sourcemap

执行以下命令可一键生成性能报告:

npx lhci collect --url="http://localhost:3000/#/segmentation" --collect.numberOfRuns=3 --collect.chromeFlags="--no-sandbox" && npx lhci upload

提示:--collect.numberOfRuns=3多次采集取平均值,消除单次波动影响;--collect.chromeFlags解决 Docker 环境下 Chrome 启动问题。

5. 从源码到可部署产物:Vite 打包配置与生产环境适配技巧

5.1vite build输出结构分析与 CDN 资源路径修正

执行npm run build后,dist/目录生成标准静态文件:

dist/ ├── assets/ │ ├── style.css │ ├── logo.png │ └── eye5.webp ├── index.html └── assets/ ├── index-CzXyZbQk.js # 主应用 JS └── vendor-CzXyZbQk.js # 第三方库 JS

默认base/,若需部署到子路径(如https://hospital.example.com/glaucoma/),需修改vite.config.js

export default defineConfig({ base: '/glaucoma/', // 关键:设置公共基础路径 build: { outDir: 'dist', assetsDir: 'assets', rollupOptions: { output: { entryFileNames: `assets/[name]-[hash].js`, chunkFileNames: `assets/[name]-[hash].js`, assetFileNames: `assets/[name]-[hash].[ext]` } } } })

同时,index.html<script><link>标签的src/href会自动添加/glaucoma/前缀,无需手动修改。

5.2 静态资源指纹与缓存策略配置

Vite 默认开启build.rollupOptions.output.assetFileNames哈希命名,确保资源更新后浏览器强制拉取新版本。但需配合 HTTP 缓存头:

# nginx 配置片段 location /glaucoma/ { alias /var/www/glaucoma/; try_files $uri $uri/ /glaucoma/index.html; # 静态资源长期缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|webp|jfif)$ { expires 1y; add_header Cache-Control "public, immutable"; } # HTML 文件禁止缓存 location ~* \.html$ { expires -1; add_header Cache-Control "no-cache"; } }

immutable指令告知浏览器:只要 URL 不变,资源绝不会更新,可永久缓存。配合哈希文件名,实现“更新即生效”。

5.3 生产环境错误监控与 Source Map 安全控制

开发阶段vite.config.jsbuild.sourcemap = true,便于调试;生产环境应关闭:

// vite.config.js 生产配置 if (process.env.NODE_ENV === 'production') { export default defineConfig({ build: { sourcemap: false, // 关键:禁用 source map,防止源码泄露 minify: 'terser', terserOptions: { compress: { drop_console: true, // 移除 console.log drop_debugger: true } } } }) }

若需错误监控,可在main.js全局捕获:

// main.js window.addEventListener('error', (e) => { // 仅上报脚本错误,过滤资源加载错误 if (e.filename) { fetch('/api/error-log', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: e.message, filename: e.filename, lineno: e.lineno, colno: e.colno, userAgent: navigator.userAgent }) }) } })

注意:/api/error-log为占位路径,实际需对接 Sentry 或自建日志服务。本项目未实现,但框架已预留钩子。

最终打包体积(npm run build输出):

  • dist/总大小:1.24 MB
  • index-CzXyZbQk.js:386 KB(含 Vue + Router + Pinia)
  • vendor-CzXyZbQk.js:212 KB(第三方库)
  • assets/图像总和:598 KB(WebP/JPG/JFIF 混合)

使用gzip压缩后,传输体积降至 427 KB,满足医疗系统内网快速加载要求。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询