目录
背景
在现代医疗影像分析软件中,医生需要使用多种工具(如标记、线段、平移、缩放、调窗等)来辅助诊断病灶的大小、形状和恶性程度等信息。为了提升分析效率和用户体验,这类软件通常采用多Cell布局,每个 Cell 显示不同的图像视图(如多平面重建 [MPR] 和体积渲染 [VR])。
由于不同类型的 Cell 需要支持不同的工具,如何灵活、高效地管理和切换这些工具成为开发此类软件的关键任务。本文将介绍如何在 Vue 3 中,通过配置文件和工具栏(Topbar)的结合,使用模块化设计与自定义 Hook 来实现这一目标。
问题分析
在多Cell布局的软件中,不同类型的 Cell 对工具的需求各不相同。这些工具需要根据配置文件进行初始化,并在不同的 Cell 上绑定到特定的鼠标按键,以便医生可以通过鼠标操作这些工具。此外,医生需要通过点击工具栏来快速切换和激活不同的工具。因此,本文的实现方案主要解决以下问题:
- 工具配置管理:定义不同类型 Cell 支持的工具集,以及工具与鼠标按键的映射关系。
- 工具初始化与绑定:根据配置文件创建工具类实例,并将它们绑定到鼠标按键。
- 工具切换机制:通过工具栏提供的按钮,切换和激活不同的工具。
目录结构
为了实现模块化设计并保持代码的清晰性和可维护性,我们将代码组织成以下目录结构:
arduino
src/
│
├── components/
│ ├── Topbar.vue
│ └── hooks/
│ └── useToolActivation.js
│
├── tools/
│ ├── Tool.js
│ ├── PanTool.js
│ ├── ZoomTool.js
│ ├── MeasureTool.js
│ ├── WindowingTool.js
│ └── useToolFactory.js
│
└── config/
└── toolConfig.json
实现方案
4.1 配置文件管理工具
首先,我们使用 JSON 文件定义每种类型 Cell 支持的工具集,并指定工具与鼠标按键的映射关系。
toolConfig.json
json
{
"MPRCell": {
"left": ["PanTool", "ZoomTool"],
"right": ["WindowingTool"],
"mid": ["MeasureTool"],
"allowedTools": ["PanTool", "ZoomTool", "WindowingTool", "MeasureTool"]
},
"VRCell": {
"left": ["PanTool"],
"right": ["ZoomTool"],
"allowedTools": ["PanTool", "ZoomTool"]
}
}
在这个配置文件中:
left
:表示鼠标左键绑定的工具。right
:表示鼠标右键绑定的工具。mid
:表示鼠标中键绑定的工具。allowedTools
:列出了该 Cell 类型下所有可用的工具。
4.2 工具类与工厂模式
接下来,我们定义一个通用的工具基类 Tool
,并为每个具体工具(如 PanTool
、ZoomTool
等)创建单独的文件。这些工具类将继承 Tool
基类,并实现各自的操作逻辑。我们还通过工厂函数 useToolFactory
动态创建工具实例。
Tool.js
js
export class Tool {
constructor(cell, button) {
this.cell = cell;
this.button = button; // 绑定的鼠标按键
}
bindMouseEvents() {
this.handleMouseDown = (e) => {
if (e.button === this.button) {
this.performAction();
}
};
document.addEventListener('mousedown', this.handleMouseDown);
}
unbindMouseEvents() {
document.removeEventListener('mousedown', this.handleMouseDown);
}
performAction() {
// 子类实现具体的操作逻辑
}
activate() {
console.log(`${this.constructor.name} activated`);
this.bindMouseEvents();
}
deactivate() {
console.log(`${this.constructor.name} deactivated`);
this.unbindMouseEvents();
}
}
PanTool.js
javascript
import { Tool } from './Tool';
export class PanTool extends Tool {
performAction() {
console.log('PanTool action performed');
// 实现平移操作逻辑
}
}
ZoomTool.js
javascript
import { Tool } from './Tool';
export class ZoomTool extends Tool {
performAction() {
console.log('ZoomTool action performed');
// 实现缩放操作逻辑
}
}
useToolFactory.js
javascript
import { ref } from 'vue';
import { PanTool } from './PanTool';
import { ZoomTool } from './ZoomTool';
import { MeasureTool } from './MeasureTool';
import { WindowingTool } from './WindowingTool';
export function useToolFactory() {
const tools = ref({});
const createTool = (toolName, cell, button) => {
switch (toolName) {
case 'PanTool':
return new PanTool(cell, button);
case 'ZoomTool':
return new ZoomTool(cell, button);
case 'MeasureTool':
return new MeasureTool(cell, button);
case 'WindowingTool':
return new WindowingTool(cell, button);
default:
throw new Error(`Unknown tool: ${toolName}`);
}
};
const initTools = (config, cell) => {
['left', 'right', 'mid'].forEach((buttonName, buttonIndex) => {
config[buttonName].forEach(toolName => {
tools.value[toolName] = createTool(toolName, cell, buttonIndex);
});
});
};
return {
tools,
initTools,
};
}
4.3 自定义 Hook 实现工具激活
通过自定义 Hook useToolActivation
,我们管理工具的激活状态,并自动绑定和解绑鼠标事件。
useToolActivation.js
javascript
import { ref } from 'vue';
export function useToolActivation(tools) {
const activeTool = ref(null);
const activateTool = (toolName) => {
if (activeTool.value) {
activeTool.value.deactivate(); // 停用之前激活的工具并解绑事件
}
const tool = tools.value[toolName];
if (tool) {
activeTool.value = tool;
activeTool.value.activate(); // 激活新工具并绑定事件
}
};
return {
activeTool,
activateTool,
};
}
4.4 工具栏组件实现
工具栏组件 Topbar.vue
展示了所有可用的工具按钮,并通过点击按钮来激活不同的工具。
Topbar.vue
vue
<template>
<div id="topbar">
<button @click="activateTool('PanTool')">平移</button>
<button @click="activateTool('ZoomTool')">缩放</button>
<button @click="activateTool('MeasureTool')">测量</button>
<button @click="activateTool('WindowingTool')">调窗</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { useToolActivation } from '../hooks/useToolActivation';
import { useToolFactory } from '../../tools/useToolFactory';
import toolConfig from '../../config/toolConfig.json';
// 假设 cell 是一个响应式对象,可以从其他地方传递过来
const cell = ref(null);
// 初始化工具集
const { tools, initTools } = useToolFactory();
initTools(toolConfig.MPRCell, cell.value); // 根据配置初始化工具
// 使用工具激活 Hook
const { activateTool } = useToolActivation(tools);
// 在模板中使用 activateTool 进行工具切换
</script>
<style scoped>
#topbar {
display: flex;
gap: 10px;
}
button {
padding: 10px;
cursor: pointer;
}
</style>
总结
通过模块化设计,将工具类、工厂函数和自定义 Hook 分别放在不同的文件中,我们实现了高效且灵活的工具管理。这种结构使得代码更加清晰和易于维护,确保了软件在面对复杂的工具切换场景时依然能够保持良好的可扩展性。每个工具类的职责更加明确,通过工厂函数灵活创建工具实例,并根据配置文件进行初始化和绑定,实现了灵活且高效的工具管理。这种设计模式非常适合应用在需要复杂交互操作的医疗影像分析软件中。