123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- <template>
- <Teleport :to="`#${DomMountId}`" v-if="stage">
- <transition name="pointer-fade">
- <div
- v-if="pointer"
- :style="{ transform: pointer }"
- :size="8"
- class="propertys-controller"
- ref="layout"
- >
- <ElMenu>
- <ElMenuItem v-for="menu in menus" @click="clickHandler(menu.handler)">
- <span class="menu-item">{{ menu.label }}</span>
- </ElMenuItem>
- </ElMenu>
- </div>
- </transition>
- </Teleport>
- </template>
- <script lang="ts" setup>
- import { computed, nextTick, ref, watch } from "vue";
- import { useMouseMenusFilter, useStage } from "../../hook/use-global-vars.ts";
- import { useMode } from "../../hook/use-status.ts";
- import { DC, EntityShape } from "@/deconstruction.js";
- import { useViewerTransformConfig } from "../../hook/use-viewer.ts";
- import { Transform } from "konva/lib/Util";
- import { DomMountId } from "@/constant/index.ts";
- import { ElMenu, ElMenuItem } from "element-plus";
- import { Mode } from "@/constant/mode.ts";
- import { useGlobalOnlyRightClickShape } from "@/core/hook/use-event.ts";
- import { useStore } from "@/core/store/index.ts";
- const props = defineProps<{
- target: DC<EntityShape> | undefined;
- data?: Record<string, any>;
- menus: Array<{ icon?: any; label?: string; handler: () => void }>;
- }>();
- const emit = defineEmits<{
- (e: "show" | "hide"): void;
- }>();
- const layout = ref<HTMLDivElement>();
- const stage = useStage();
- const { getFilter } = useMouseMenusFilter();
- const store = useStore();
- const id = computed(() => props.target?.getNode().id());
- const type = computed(() => id.value && store.getType(id.value));
- const menus = computed(() => {
- if (id.value && type.value) {
- return getFilter(type.value, id.value)(props.menus);
- } else {
- return props.menus;
- }
- });
- const show = ref(false);
- const { add } = useGlobalOnlyRightClickShape();
- const mode = useMode();
- watch(
- () => !mode.value.has(Mode.draw) && props.target?.getNode(),
- (shape, _, onCleanup) => {
- if (!shape) return;
- const del = add(
- shape,
- async () => {
- await nextTick();
- show.value = true;
- },
- () => {
- show.value = false;
- }
- );
- onCleanup(del);
- }
- );
- const clickHandler = (handler: () => void) => {
- handler();
- show.value = false;
- };
- const move = new Transform();
- const pointer = ref<string | null>(null);
- const calcPointer = async () => {
- if (!show.value) {
- pointer.value = null;
- return;
- } else if (pointer.value) {
- return;
- }
- const $stage = stage.value!.getStage();
- const mousePosition = $stage.pointerPos;
- if (!mousePosition) {
- pointer.value = null;
- return;
- }
- const $shape = props.target!.getNode();
- const shapeRect = $shape.getClientRect();
- const shapeR = shapeRect.x + shapeRect.width;
- const shapeB = shapeRect.y + shapeRect.height;
- let x = Math.min(Math.max(mousePosition.x, shapeRect.x), shapeR);
- let y = Math.min(Math.max(mousePosition.y, shapeRect.y), shapeB);
- move.reset();
- move.translate(x, y);
- pointer.value = `matrix(${move.m.join(",")})`;
- await nextTick();
- const domRect = layout.value!.getBoundingClientRect();
- x = x - domRect.width / 2;
- x = Math.max(x, shapeRect.x);
- if (x + domRect.width > shapeR) {
- x = shapeR - domRect.width;
- }
- if (y + domRect.height > shapeB) {
- y = y - domRect.height;
- }
- x = Math.max(x, 10);
- y = Math.max(y, 10);
- if (x + domRect.width > $stage.width() - 10) {
- x = $stage.width() - domRect.width - 10;
- }
- if (y + domRect.height > $stage.height() - 10) {
- y = $stage.height() - domRect.height - 10;
- }
- move.reset();
- move.translate(x, y);
- pointer.value = `matrix(${move.m.join(",")})`;
- };
- watch(
- () => !!pointer.value,
- (show) => {
- emit(show ? "show" : "hide");
- }
- );
- let timeout: any;
- const resetPointer = () => {
- if (!show.value) {
- pointer.value = null;
- return;
- } else if (pointer.value) {
- return;
- }
- clearTimeout(timeout);
- timeout = setTimeout(calcPointer, 16);
- };
- watch(show, resetPointer);
- watch(useViewerTransformConfig(), () => {
- pointer.value = null;
- resetPointer();
- });
- </script>
- <style lang="scss" scoped>
- .menu-item {
- display: block;
- min-width: 60px;
- margin-left: 10px;
- }
- </style>
- <style lang="scss">
- .propertys-controller {
- pointer-events: none;
- position: absolute;
- border-radius: 4px;
- border: 1px solid #e4e7ed;
- background-color: #ffffff;
- left: 0;
- top: 0;
- overflow: hidden;
- color: #303133;
- display: flex;
- flex-direction: column;
- align-items: center;
- box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
- min-width: 10px;
- padding: 5px 0;
- pointer-events: all;
- font-size: 14px;
- border: 1px solid var(--el-border-color-light);
- box-shadow: var(--el-dropdown-menu-box-shadow);
- background-color: var(--el-bg-color-overlay);
- border-radius: var(--el-border-radius-base);
- --el-menu-base-level-padding: 16px;
- --el-menu-item-height: 32px;
- --el-menu-item-font-size: 14px;
- .el-menu-item {
- align-items: center;
- padding: 5px 16px 5px 6px !important;
- color: var(--el-text-color-regular);
- }
- }
- .pointer-fade-enter-active,
- .pointer-fade-leave-active {
- transition: opacity 0.3s ease;
- .item {
- pointer-events: none;
- }
- }
- .pointer-fade-enter-from,
- .pointer-fade-leave-to {
- opacity: 0;
- }
- </style>
|