| 
					
				 | 
			
			
				@@ -0,0 +1,236 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  <Dialog v-model="dialogVisible" title="设置热区" width="780" @close="handleClose"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <div ref="container" class="relative h-full w-750px"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <el-image :src="imgUrl" class="pointer-events-none h-full w-750px select-none" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <div 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        v-for="(item, hotZoneIndex) in formData" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        :key="hotZoneIndex" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        class="hot-zone" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        :style="{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          width: `${item.width}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          height: `${item.height}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          top: `${item.top}px`, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          left: `${item.left}px` 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        @mousedown="handleMove(item, $event)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        @dblclick="handleShowAppLinkDialog(item)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      > 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <span class="pointer-events-none select-none">{{ item.name || '双击选择链接' }}</span> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <Icon icon="ep:close" class="delete" :size="14" @click="handleRemove(item)" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <!-- 8个控制点 --> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <span 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          class="ctrl-dot" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          v-for="(dot, dotIndex) in CONTROL_DOT_LIST" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          :key="dotIndex" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          :style="dot.style" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          @mousedown="handleResize(item, dot, $event)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ></span> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </div> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    <template #footer> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <el-button @click="handleAdd" type="primary" plain> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <Icon icon="ep:plus" class="mr-5px" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        添加热区 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </el-button> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      <el-button @click="handleSubmit" type="primary" plain> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        <Icon icon="ep:check" class="mr-5px" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        确定 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      </el-button> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    </template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  </Dialog> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  <AppLinkSelectDialog ref="appLinkDialogRef" @app-link-change="handleAppLinkChange" /> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</template> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<script setup lang="ts"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { HotZoneItemProperty } from '@/components/DiyEditor/components/mobile/HotZone/config' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { array, string } from 'vue-types' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CONTROL_DOT_LIST, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CONTROL_TYPE_ENUM, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ControlDot, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  HOT_ZONE_MIN_SIZE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  useDraggable, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  zoomIn, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  zoomOut 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} from './controller' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { AppLink } from '@/components/AppLinkInput/data' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import { remove } from 'lodash-es' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** 热区编辑对话框 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+defineOptions({ name: 'HotZoneEditDialog' }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 定义属性 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const props = defineProps({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  modelValue: array<HotZoneItemProperty>(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  imgUrl: string().def('') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const emit = defineEmits(['update:modelValue']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const formData = ref<HotZoneItemProperty[]>([]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 弹窗的是否显示 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const dialogVisible = ref(false) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 打开弹窗 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const open = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 放大 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  formData.value = zoomIn(props.modelValue) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  dialogVisible.value = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 提供 open 方法,用于打开弹窗 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+defineExpose({ open }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 热区容器 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const container = ref<HTMLDivElement>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 增加热区 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleAdd = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  formData.value.push({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    width: HOT_ZONE_MIN_SIZE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    height: HOT_ZONE_MIN_SIZE, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    top: 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    left: 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } as HotZoneItemProperty) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 删除热区 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleRemove = (hotZone: HotZoneItemProperty) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  remove(formData.value, hotZone) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 移动热区 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleMove = (item: HotZoneItemProperty, e: MouseEvent) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  useDraggable(item, e, (left, top, _, __, moveWidth, moveHeight) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    setLeft(item, left + moveWidth) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    setTop(item, top + moveHeight) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 调整热区大小、位置 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleResize = (item: HotZoneItemProperty, ctrlDot: ControlDot, e: MouseEvent) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  useDraggable(item, e, (left, top, width, height, moveWidth, moveHeight) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ctrlDot.types.forEach((type) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      switch (type) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case CONTROL_TYPE_ENUM.LEFT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          setLeft(item, left + moveWidth) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case CONTROL_TYPE_ENUM.TOP: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          setTop(item, top + moveHeight) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case CONTROL_TYPE_ENUM.WIDTH: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 上移时,高度为减少 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const direction = ctrlDot.types.includes(CONTROL_TYPE_ENUM.LEFT) ? -1 : 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            setWidth(item, width + moveWidth * direction) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        case CONTROL_TYPE_ENUM.HEIGHT: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // 左移时,宽度为减少 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const direction = ctrlDot.types.includes(CONTROL_TYPE_ENUM.TOP) ? -1 : 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            setHeight(item, height + moveHeight * direction) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+          break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 设置X轴坐标 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const setLeft = (item: HotZoneItemProperty, left: number) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 不能超出容器 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (left >= 0 && left <= container.value!.offsetWidth - item.width) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    item.left = left 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 设置Y轴坐标 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const setTop = (item: HotZoneItemProperty, top: number) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 不能超出容器 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (top >= 0 && top <= container.value!.offsetHeight - item.height) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    item.top = top 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 设置宽度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const setWidth = (item: HotZoneItemProperty, width: number) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 不能小于最小宽度 && 不能超出容器右边 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (width >= HOT_ZONE_MIN_SIZE && item.left + width <= container.value!.offsetWidth) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    item.width = width 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 设置高度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const setHeight = (item: HotZoneItemProperty, height: number) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 不能小于最小高度 && 不能超出容器底部 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (height >= HOT_ZONE_MIN_SIZE && item.top + height <= container.value!.offsetHeight) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    item.height = height 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 处理对话框关闭 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleSubmit = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 会自动触发handleClose 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  dialogVisible.value = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+// 处理对话框关闭 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleClose = () => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // 缩小 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const list = zoomOut(formData.value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  emit('update:modelValue', list) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const activeHotZone = ref<HotZoneItemProperty>() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const appLinkDialogRef = ref() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleShowAppLinkDialog = (hotZone: HotZoneItemProperty) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  activeHotZone.value = hotZone 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  appLinkDialogRef.value.open(hotZone.url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const handleAppLinkChange = (appLink: AppLink) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (!appLink || !activeHotZone.value) return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  activeHotZone.value.name = appLink.name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  activeHotZone.value.url = appLink.path 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</script> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+<style scoped lang="scss"> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+.hot-zone { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  background: var(--el-color-primary-light-7); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  opacity: 0.8; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  border: 1px solid var(--el-color-primary); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  color: var(--el-color-primary); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  font-size: 16px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  display: flex; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  align-items: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  justify-content: center; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  cursor: move; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  z-index: 10; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* 控制点 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  .ctrl-dot { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    width: 8px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    height: 8px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    border-radius: 50%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    border: inherit; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    background-color: #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    z-index: 11; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  .delete { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    display: none; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    position: absolute; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    top: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    right: 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    padding: 2px 2px 6px 6px; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    background-color: var(--el-color-primary); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    border-radius: 0 0 0 80%; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    cursor: pointer; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    color: #fff; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    text-align: right; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  &:hover { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    .delete { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      display: block; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+</style> 
			 |