<template> <div class="right-click-menu"> <div ref="RightMenuBody" class="right-menu-body" :style="styles" v-clickoutside="close" v-if="visible" > <ul> <template v-if="!IS_NOT_TOPIC"> <li @click="toEdit"> <i class="el-icon-edit-outline"></i> {{ IS_CONTAINER_ELEMENT ? "编辑元素" : "编辑大题" }} </li> <li class="li-danger" @click="toDelete"> <i class="el-icon-delete"></i> {{ IS_CONTAINER_ELEMENT ? "删除元素" : "删除大题" }} </li> </template> <li v-if="IS_CONTAINER_ELEMENT && (IS_EXPLAIN || IS_COMPOSITION)" @click="toCopyExplainElement" > <i class="el-icon-copy-document"></i> 复制元素 </li> <li v-if="(IS_EXPLAIN || IS_COMPOSITION) && curCopyElement" @click="toPasteExplainElement" > <i class="el-icon-document-copy"></i> 粘贴元素 </li> <template v-if="IS_EXPLAIN || IS_COMPOSITION"> <li @click="toCopyChildren"> <i class="el-icon-circle-plus-outline"></i> 新增答题区 </li> <li class="li-danger" @click="toDeleteChildren" v-if="showDeleteChildBtn" > <i class="el-icon-delete"></i> 删除答题区 </li> </template> <li v-if="CAN_MOVE_UP" @click="toMoveUpTopic"> <i class="el-icon-upload2"></i> 上移大题 </li> <li v-if="CAN_MOVE_DOWN" @click="toMoveDownTopic"> <i class="el-icon-download"></i> 下移大题 </li> <li v-if="!IS_CONTAINER_ELEMENT && !IS_NOT_TOPIC" @click="toInsetTopic"> <i class="el-icon-add-location"></i> 插入大题 </li> <li @click="toInsertForbidAnswer"> <i class="el-icon-crop"></i> 插入禁答区 </li> <li v-if="IS_NOT_TOPIC" class="li-danger" @click="toDelete"> <i class="el-icon-delete"></i> 删除禁答区 </li> </ul> </div> </div> </template> <script> import { mapState, mapMutations, mapActions } from "vuex"; import { deepCopy } from "../plugins/utils"; import { fetchSameSerialNumberChildrenPositionInfo } from "../store/card"; import Clickoutside from "element-ui/src/utils/clickoutside"; import { getElementModel, EDITABLE_NOT_TOPIC } from "../elementModel"; export default { name: "right-click-menu", directives: { Clickoutside }, data() { return { visible: false, showDeleteChildBtn: false, curCopyElement: null, styles: { position: "fixed", zIndex: 3000, }, }; }, computed: { ...mapState("card", ["curElement", "topics", "topicSeries"]), IS_CONTAINER_ELEMENT() { return !!this.curElement.container; }, IS_EXPLAIN() { return ( this.curElement.type === "EXPLAIN" || (this.curElement.container && this.curElement.container.type === "EXPLAIN") ); }, IS_COMPOSITION() { return ( this.curElement.type === "COMPOSITION" || (this.curElement.container && this.curElement.container.type === "COMPOSITION") ); }, CAN_MOVE_UP() { if (this.IS_CONTAINER_ELEMENT || this.IS_NOT_TOPIC) return false; const curTopicPos = this.topicSeries.findIndex( (item) => item.id === this.curElement.parent.id ); return ( curTopicPos && this.topicSeries[curTopicPos - 1].sign === this.topicSeries[curTopicPos].sign ); }, CAN_MOVE_DOWN() { if (this.IS_CONTAINER_ELEMENT || this.IS_NOT_TOPIC) return false; const curTopicPos = this.topicSeries.findIndex( (item) => item.id === this.curElement.parent.id ); return ( curTopicPos !== this.topicSeries.length - 1 && this.topicSeries[curTopicPos + 1].sign === this.topicSeries[curTopicPos].sign ); }, IS_NOT_TOPIC() { return EDITABLE_NOT_TOPIC.includes(this.curElement.type); }, }, mounted() { this.init(); }, methods: { ...mapMutations("card", ["setOpenElementEditDialog"]), ...mapActions("card", [ "actElementById", "removeElement", "removeElementChild", "pasteExplainElementChild", "rebuildPages", "copyExplainChildren", "deleteExplainChildren", "topicMoveUp", "addForbidArea", "scrollToElementPage", ]), init() { // 注册自定义右键事件菜单 document.oncontextmenu = function () { return false; }; document.addEventListener("mouseup", this.docMouseUp); }, close() { this.visible = false; }, show() { this.visible = true; }, docMouseUp(e) { if (e.button === 2) { this.rightClick(e); } }, rightClick(e) { const id = this.getRelateElementId(e.target); if (!id) return; this.actElementById(id); let curElement = this.curElement; const TYPES = ["EXPLAIN", "COMPOSITION"]; if ( TYPES.includes(curElement.type) || (curElement.container && TYPES.includes(curElement.container.type)) ) { if (curElement.container) { const pos = this.topics.findIndex( (item) => item.id === curElement.container.id ); curElement = this.topics[pos]; } const positionInfos = fetchSameSerialNumberChildrenPositionInfo( curElement, this.topics ); this.showDeleteChildBtn = positionInfos.length >= 2; } this.show(); this.$nextTick(() => { const { x: clickLeft, y: clickTop } = e; const { offsetWidth: menuWidth, offsetHeight: menuHeight } = this.$refs.RightMenuBody; const { innerWidth: wWidth, innerHeight: wHeight } = window; let menuLeft = clickLeft, menuTop = clickTop; if (menuWidth + clickLeft > wWidth) { menuLeft = clickLeft - menuWidth; } if (menuHeight + clickTop > wHeight) { menuTop = clickTop - menuHeight; } this.styles = Object.assign({}, this.styles, { top: menuTop + "px", left: menuLeft + "px", }); }); }, getRelateElementId(dom) { let parentNode = dom; while ( !( (parentNode["id"] && parentNode["id"].includes("element-")) || parentNode.className.includes("page-column-body") ) ) { parentNode = parentNode.parentNode; } const elementType = parentNode.getAttribute("data-type"); const unValidElement = ["TOPIC_HEAD", "CARD_HEAD"]; return parentNode["id"] && elementType && !unValidElement.includes(elementType) ? parentNode["id"] : null; }, toEdit() { this.curElement._edit = true; this.close(); this.setOpenElementEditDialog(true); }, toDelete() { this.close(); if (this.IS_NOT_TOPIC) { this.removeSelectElement(); return; } const name = this.IS_CONTAINER_ELEMENT ? "元素" : "大题"; this.$confirm(`确定要删除当前${name}吗?`, "提示", { type: "warning", }) .then(() => { this.removeSelectElement(); }) .catch(() => {}); }, toCopyChildren() { this.close(); this.copyExplainChildren(this.curElement); this.toRebuildPages(); }, toDeleteChildren() { this.close(); this.deleteExplainChildren(this.curElement); this.toRebuildPages(); }, removeSelectElement() { if (this.curElement["container"]) { this.removeElementChild(this.curElement); } else { this.removeElement(this.curElement); } this.toRebuildPages(); }, toCopyExplainElement() { this.close(); this.curCopyElement = deepCopy(this.curElement); }, toPasteExplainElement() { this.close(); const id = this.curElement.container ? this.curElement.container.id : this.curElement.id; const pasteElement = this.curCopyElement.container.id === id ? Object.assign({}, this.curCopyElement, { y: this.curCopyElement.y + 20, }) : this.curCopyElement; this.pasteExplainElementChild({ curElement: this.curElement, pasteElement, }); this.toRebuildPages(); }, toMoveUpTopic() { this.close(); this.topicMoveUp(this.curElement.parent.id); this.toRebuildPages(); }, toMoveDownTopic() { this.close(); const curTopicPos = this.topicSeries.findIndex( (item) => item.id === this.curElement.parent.id ); this.topicMoveUp(this.topicSeries[curTopicPos + 1].id); this.toRebuildPages(); }, toInsetTopic() { this.close(); this.$emit("inset-topic", { id: this.curElement.parent.id, type: this.curElement.type, }); }, toInsertForbidAnswer() { this.close(); let element = getElementModel("FORBID_AREA"); element.w = document.getElementById("topic-column").offsetWidth; element.sign = this.curElement.sign; this.addForbidArea({ element, beforeElementId: this.curElement.id }); this.$nextTick(() => { this.rebuildPages(); this.$nextTick(() => { this.scrollToElementPage(element); }); }); }, toRebuildPages() { this.$nextTick(() => { this.rebuildPages(); }); }, }, beforeDestroy() { document.oncontextmenu = null; document.removeEventListener("mouseup", this.docMouseUp); }, }; </script>