完整效果:
cornerstone3D的dicom影像浏览器
本章效果预览:

1.在views/home/index.vue中:
<template>
<div class="container flex-vertical" ref="view2d">
<div class="header flex-horizontal">
<div class="logo">
logo
</div>
<div class="study-list">
study list
</div>
</div>
<div class="main" :class="mainClass">
<div :class="toolbarClass" :style="toolbarStyle">
</div>
<div class="nav-display" :class="navDisplayClass">
<div :class="navClass" :style="navbarStyle">
series list
</div>
<div class="display-area">
<displayer-area ref="displayArea"></displayer-area>
</div>
</div>
</div>
<div class="footer">study bar</div>
</div>
</template>
<script setup name="View2d">
import { ref, onMounted, computed } from "vue";
import displayerArea from "@/components/displayerArea/index.vue";
const view2d = ref(null);
const navbarHW = ref(146);
const toolbarHW = ref(300);
const toolbarPos = ref("right"); // top or right
const navbarPos = ref("lef"); // bottom or left
const mainClass = computed(() => {
const vorh = toolbarPos.value === "top" ? "vertical" : "horizontal-reverse";
return "flex-" + vorh;
});
const toolbarClass = computed(() => {
return "toolbar-" + toolbarPos.value;
});
const toolbarStyle = computed(() => {
if (toolbarPos.value === "top") {
return {
height: toolbarHW.value + "px",
};
} else {
return {
width: toolbarHW.value + "px",
};
}
});
const navDisplayClass = computed(() => {
const vorh = navbarPos.value === "bottom" ? "vertical-reverse" : "horizontal";
return "flex-" + vorh;
});
const navClass = computed(() => {
return "nav-" + navbarPos.value;
});
const navbarStyle = computed(() => {
if (navbarPos.value === "bottom") {
return {
height: navbarHW.value + "px",
};
} else {
return {
width: navbarHW.value + "px",
};
}
});
</script>
<style scoped lang="scss">
$bg-color: #333;
$main-color: lightblue;
// $header-color: #108ee9;
$header-color: #45b7ec;
$footer-color: #45b7ec;
$resizer-width: 2px;
$header-height: 50px;
$footer-height: 100px;
.container {
background-color: $bg-color;
height: 100vh;
width: 100vw;
max-width: 100%;
max-height: 100%;
justify-content: center;
// align-items: center;
text-align: center;
color: black;
user-select: none;
}
.flex-vertical {
display: flex;
flex-direction: column;
}
.flex-vertical-reverse {
display: flex;
flex-direction: column-reverse;
}
.flex-horizontal {
display: flex;
flex-direction: row;
}
.flex-horizontal-reverse {
display: flex;
flex-direction: row-reverse;
}
.resizer-vertical {
cursor: ew-resize;
width: $resizer-width;
background-color: gray;
z-index: 10;
}
.resizer-horizontal {
cursor: ns-resize;
height: $resizer-width;
background-color: gray;
width: 100%;
z-index: 10;
}
.header {
height: $header-height;
background-color: $header-color;
border-bottom: gray solid 1px;
.logo {
height: 100%;
width: 200px;
border-right: gray solid 1px;
flex-shrink: 0;
}
.study-list {
flex: 1;
height: 100%;
// background-color: lightblue;
}
}
.main {
width: 100%;
height: calc(100vh - $header-height - $footer-height);
background-color: $main-color;
.nav-display {
flex: 1;
}
.toolbar-top {
height: 200px;
width: 100%;
}
.toolbar-right {
width: 200px;
height: 100%;
}
.nav-display {
.display-area {
flex: 1;
flex-shrink: 0;
}
.nav-left {
height: 100%;
}
.nav-bottom {
width: 100%;
}
}
}
.footer {
background-color: $footer-color;
height: $footer-height;
border-top: gray solid 1px;
}
</style>
2.displayerArea组件:
<script lang="js" setup name="DisplayerArea">
import { ref, onMounted, computed, reactive, nextTick } from "vue";
import Displayer from "@/components/Displayer/index.vue";
import { useAppStore } from "@/store/appStore.js";
import config from "@/config/index.js"
const appStore = useAppStore();
const { layout, pageSize } = appStore.$state
const dispRefs = reactive([]);
const selected = [];
const maxDisp = null;
const domWidth = ref(0);
const containerStyle = computed(() => {
let result = {
display: 'gird',
'grid-template-columns': repeat(layout.row, "1fr"),
height: '100%'
};
return result
});
const repeat = (n, s) => {
let dst = "";
for (let i = 0; i < n; i++) {
dst += s + " ";
}
return dst;
};
const getDispRef = (el, pos) => {
if (el) {
dispRefs[pos] = el;
}
};
const setLayout = (row, col) => {
appStore.setLayout(row, col)
selected.length = 0;
};
onMounted(() => {
const { row, col } = config.defLayout
setLayout(row, col);
});
</script>
<template>
<div class="displayarea" :style="containerStyle">
<Displayer
v-for="(v, idx) in pageSize"
:key="idx"
:ref="el => getDispRef(el, idx)"
:pos="idx"
/>
</div>
</template>
<style lang="scss" scoped>
.displayarea{
display: grid;
grid-gap: 1px 1px;
background-color: black;
color: #fff;
}
</style>
3.Displayer
<script lang="js" setup name="Displayer">
import { ref, reactive, onMounted, computed, getCurrentInstance, onUnmounted } from "vue";
const IsSel = ref(false);
const borderClass = computed(() => {
let s = "selected";
if (IsSel.value) {
s = "selected";
} else {
if (IsHover.value) {
s = "hovered";
} else {
s = "unselect";
}
}
return s;
});
</script>
<template>
<div
class="displaybox"
:class="borderClass"
>
<div class="displayer" ref="displayer">
</div>
</div>
</template>
<style lang="scss" scoped>
.displaybox {
position: relative;
display: flex;
flex-direction: row;
background-color: black;
.scroll-right {
width: 20px;
}
}
.displayer {
flex: 1;
text-align: left;
cursor: default;
user-select: none;
$font-size: 14px;
@mixin orient($text-align: left) {
position: absolute;
color: white;
font-size: $font-size;
text-align: $text-align;
z-index: 10;
}
.orient_top {
@include orient(center);
top: 2px;
left: calc(50% - 30px);
width: 60px;
}
.orient_bottom {
@include orient(center);
bottom: 2px;
left: calc(50% - 30px);
width: 60px;
}
.orient_left {
@include orient();
top: calc(50% - 20px);
left: 2px;
}
.orient_right {
@include orient();
top: calc(50% - 20px);
right: 2px;
}
}
.selected {
border: 1px solid red;
}
.hovered {
border: 1px dashed yellow;
}
.unselect {
border: 1px solid #fff;
}
</style>
4.app store
import { defineStore } from "pinia";
export const useAppStore = defineStore("app", {
state: () => {
return {
layout: {
col: 2,
row: 2
},
pageSize: 4
}
},
actions:{
setLayout(row, col){
this.layout.col = col
this.layout.row = row
}
}
})