基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构
完整效果:
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 leftconst 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.$stateconst 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"><Displayerv-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><divclass="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 = colthis.layout.row = row}}
})