|
@@ -1,5 +1,6 @@
|
|
|
<template>
|
|
|
<div
|
|
|
+ ref="courserware"
|
|
|
class="courserware"
|
|
|
:style="[
|
|
|
{
|
|
@@ -20,17 +21,51 @@
|
|
|
<div :key="j" :class="['col', `col-${i}-${j}`]" :style="computedColStyle(col)">
|
|
|
<!-- 网格 -->
|
|
|
<template v-for="(grid, k) in col.grid_list">
|
|
|
- <component
|
|
|
- :is="previewComponentList[grid.type]"
|
|
|
- ref="preview"
|
|
|
- :key="k"
|
|
|
- :content="computedColContent(grid.id)"
|
|
|
- :class="[grid.id]"
|
|
|
- :style="{
|
|
|
- gridArea: grid.grid_area,
|
|
|
- height: grid.height,
|
|
|
- }"
|
|
|
- />
|
|
|
+ <div :key="k" @contextmenu.prevent="handleContextMenu($event, grid.id)">
|
|
|
+ <component
|
|
|
+ :is="previewComponentList[grid.type]"
|
|
|
+ ref="preview"
|
|
|
+ :content="computedColContent(grid.id)"
|
|
|
+ :class="[grid.id]"
|
|
|
+ :style="{
|
|
|
+ gridArea: grid.grid_area,
|
|
|
+ height: grid.height,
|
|
|
+ }"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div
|
|
|
+ v-if="showMenu && componentId === grid.id"
|
|
|
+ :key="'menu' + grid.id + k"
|
|
|
+ class="custom-context-menu"
|
|
|
+ :style="{ left: menuPosition.x + 'px', top: menuPosition.y + 'px' }"
|
|
|
+ @click="handleMenuItemClick"
|
|
|
+ >
|
|
|
+ 添加批注
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ v-if="showRemark && Object.keys(componentRemarkObj).length !== 0 && componentRemarkObj[grid.id]"
|
|
|
+ :key="'show' + grid.id + k"
|
|
|
+ >
|
|
|
+ <el-popover
|
|
|
+ v-for="(items, indexs) in componentRemarkObj[grid.id]"
|
|
|
+ :key="indexs"
|
|
|
+ placement="bottom"
|
|
|
+ width="200"
|
|
|
+ trigger="click"
|
|
|
+ >
|
|
|
+ <div v-html="items.content"></div>
|
|
|
+ <template #reference>
|
|
|
+ <SvgIcon
|
|
|
+ slot="reference"
|
|
|
+ icon-class="icon-info"
|
|
|
+ size="24"
|
|
|
+ class="remark-info"
|
|
|
+ :style="{ left: items.position_x - 12 + 'px', top: items.position_y - 12 + 'px' }"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-popover>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -63,6 +98,18 @@ export default {
|
|
|
type: Array,
|
|
|
required: true,
|
|
|
},
|
|
|
+ canRemark: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ showRemark: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false,
|
|
|
+ },
|
|
|
+ componentRemarkObj: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
},
|
|
|
data() {
|
|
|
return {
|
|
@@ -70,7 +117,26 @@ export default {
|
|
|
bookInfo: {
|
|
|
theme_color: '',
|
|
|
},
|
|
|
+ showMenu: false,
|
|
|
+ divPosition: {
|
|
|
+ left: 0,
|
|
|
+ top: 0,
|
|
|
+ }, // courserware盒子原始距离页面顶部和左边的距离
|
|
|
+ menuPosition: { x: 0, y: 0, select_node: '' }, // 用于存储菜单的位置
|
|
|
+ componentId: '', // 添加批注的组件id
|
|
|
+ };
|
|
|
+ },
|
|
|
+ mounted() {
|
|
|
+ const element = this.$refs.courserware;
|
|
|
+ const rect = element.getBoundingClientRect();
|
|
|
+ this.divPosition = {
|
|
|
+ left: rect.left,
|
|
|
+ top: rect.top,
|
|
|
};
|
|
|
+ window.addEventListener('mousedown', this.handleMouseDown);
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ window.removeEventListener('mousedown', this.handleMouseDown);
|
|
|
},
|
|
|
methods: {
|
|
|
/**
|
|
@@ -199,12 +265,48 @@ export default {
|
|
|
gridTemplateRows,
|
|
|
};
|
|
|
},
|
|
|
+ handleContextMenu(event, id) {
|
|
|
+ if (this.canRemark) {
|
|
|
+ event.preventDefault(); // 阻止默认的上下文菜单显示
|
|
|
+ this.menuPosition = {
|
|
|
+ x: event.clientX - this.divPosition.left,
|
|
|
+ y: event.clientY - this.divPosition.top,
|
|
|
+ }; // 设置菜单位置
|
|
|
+ this.componentId = id;
|
|
|
+ this.$emit('computeScroll');
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleResult(top, left, select_node) {
|
|
|
+ this.menuPosition = {
|
|
|
+ x: this.menuPosition.x + left,
|
|
|
+ y: this.menuPosition.y + top,
|
|
|
+ select_node,
|
|
|
+ }; // 设置菜单位置
|
|
|
+ this.showMenu = true; // 显示菜单
|
|
|
+ },
|
|
|
+ handleMenuItemClick() {
|
|
|
+ this.showMenu = false; // 隐藏菜单
|
|
|
+ this.$emit(
|
|
|
+ 'addRemark',
|
|
|
+ this.menuPosition.select_node,
|
|
|
+ this.menuPosition.x,
|
|
|
+ this.menuPosition.y,
|
|
|
+ this.componentId,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ handleMouseDown(event) {
|
|
|
+ if (event.button === 0 && event.target.className !== 'custom-context-menu') {
|
|
|
+ // 0 表示左键
|
|
|
+ this.showMenu = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.courserware {
|
|
|
+ position: relative;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
row-gap: 6px;
|
|
@@ -227,5 +329,22 @@ export default {
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .custom-context-menu,
|
|
|
+ .remark-info {
|
|
|
+ position: absolute;
|
|
|
+ z-index: 999;
|
|
|
+ display: flex;
|
|
|
+ gap: 3px;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 14px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+
|
|
|
+ .custom-context-menu {
|
|
|
+ padding-left: 30px;
|
|
|
+ background: url("../../../../assets/icon-publish.png") left center no-repeat;
|
|
|
+ background-size: 24px;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|