基于cornerstone3D的dicom影像浏览器 第二章,初始化页面结构

发布于:2025-07-16 ⋅ 阅读:(12) ⋅ 点赞:(0)

完整效果:

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
    }
  }
})


网站公告

今日签到

点亮在社区的每一天
去签到