feat:【mall】diy editor 的 hot-zone 代码优化(50%)

This commit is contained in:
YunaiV
2025-10-28 19:22:27 +08:00
parent a4f7a51ba0
commit 4de0050610
6 changed files with 77 additions and 69 deletions

View File

@@ -2,10 +2,9 @@ import type { StyleValue } from 'vue';
import type { HotZoneItemProperty } from '../../config'; import type { HotZoneItemProperty } from '../../config';
// 热区的最小宽高 export const HOT_ZONE_MIN_SIZE = 100; // 热区的最小宽高
export const HOT_ZONE_MIN_SIZE = 100;
// 控制的类型 /** 控制的类型 */
export enum CONTROL_TYPE_ENUM { export enum CONTROL_TYPE_ENUM {
LEFT, LEFT,
TOP, TOP,
@@ -13,14 +12,14 @@ export enum CONTROL_TYPE_ENUM {
HEIGHT, HEIGHT,
} }
// 定义热区的控制点 /** 定义热区的控制点 */
export interface ControlDot { export interface ControlDot {
position: string; position: string;
types: CONTROL_TYPE_ENUM[]; types: CONTROL_TYPE_ENUM[];
style: StyleValue; style: StyleValue;
} }
// 热区的8个控制点 /** 热区的 8 个控制点 */
export const CONTROL_DOT_LIST = [ export const CONTROL_DOT_LIST = [
{ {
position: '左上角', position: '左上角',
@@ -98,10 +97,10 @@ export const CONTROL_DOT_LIST = [
] as ControlDot[]; ] as ControlDot[];
// region 热区的缩放 // region 热区的缩放
// 热区的缩放比例 export const HOT_ZONE_SCALE_RATE = 2; // 热区的缩放比例
export const HOT_ZONE_SCALE_RATE = 2;
// 缩小:缩回适合手机屏幕的大小 /** 缩小:缩回适合手机屏幕的大小 */
export const zoomOut = (list?: HotZoneItemProperty[]) => { export function zoomOut(list?: HotZoneItemProperty[]) {
return ( return (
list?.map((hotZone) => ({ list?.map((hotZone) => ({
...hotZone, ...hotZone,
@@ -111,9 +110,10 @@ export const zoomOut = (list?: HotZoneItemProperty[]) => {
height: (hotZone.height /= HOT_ZONE_SCALE_RATE), height: (hotZone.height /= HOT_ZONE_SCALE_RATE),
})) || [] })) || []
); );
}; }
// 放大:作用是为了方便在电脑屏幕上编辑
export const zoomIn = (list?: HotZoneItemProperty[]) => { /** 放大:作用是为了方便在电脑屏幕上编辑 */
export function zoomIn(list?: HotZoneItemProperty[]) {
return ( return (
list?.map((hotZone) => ({ list?.map((hotZone) => ({
...hotZone, ...hotZone,
@@ -123,7 +123,8 @@ export const zoomIn = (list?: HotZoneItemProperty[]) => {
height: (hotZone.height *= HOT_ZONE_SCALE_RATE), height: (hotZone.height *= HOT_ZONE_SCALE_RATE),
})) || [] })) || []
); );
}; }
// endregion // endregion
/** /**

View File

@@ -10,6 +10,8 @@ import { IconifyIcon } from '@vben/icons';
import { ElButton, ElDialog, ElImage } from 'element-plus'; import { ElButton, ElDialog, ElImage } from 'element-plus';
import { AppLinkSelectDialog } from '#/views/mall/promotion/components';
import { import {
CONTROL_DOT_LIST, CONTROL_DOT_LIST,
CONTROL_TYPE_ENUM, CONTROL_TYPE_ENUM,
@@ -22,7 +24,7 @@ import {
/** 热区编辑对话框 */ /** 热区编辑对话框 */
defineOptions({ name: 'HotZoneEditDialog' }); defineOptions({ name: 'HotZoneEditDialog' });
// 定义属性 /** 定义属性 */
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Array<HotZoneItemProperty>, type: Array<HotZoneItemProperty>,
@@ -33,51 +35,53 @@ const props = defineProps({
default: '', default: '',
}, },
}); });
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = ref<HotZoneItemProperty[]>([]); const formData = ref<HotZoneItemProperty[]>([]);
// 弹窗的是否显示 const dialogVisible = ref(false); // 弹窗的是否显示
const dialogVisible = ref(false);
// 打开弹窗 /** 打开弹窗 */
const open = () => { const open = () => {
// 放大 // 放大
formData.value = zoomIn(props.modelValue); formData.value = zoomIn(props.modelValue);
dialogVisible.value = true; dialogVisible.value = true;
}; };
// 提供 open 方法,用于打开弹窗
defineExpose({ open });
// 热区容器 defineExpose({ open }); // 提供 open 方法,用于打开弹窗
const container = ref<HTMLDivElement>();
// 增加热区 const container = ref<HTMLDivElement>(); // 热区容器
const handleAdd = () => {
/** 增加热区 */
function handleAdd() {
formData.value.push({ formData.value.push({
width: HOT_ZONE_MIN_SIZE, width: HOT_ZONE_MIN_SIZE,
height: HOT_ZONE_MIN_SIZE, height: HOT_ZONE_MIN_SIZE,
top: 0, top: 0,
left: 0, left: 0,
} as HotZoneItemProperty); } as HotZoneItemProperty);
}; }
// 删除热区
const handleRemove = (hotZone: HotZoneItemProperty) => {
formData.value = formData.value.filter((item) => item !== hotZone);
};
// 移动热区 /** 删除热区 */
const handleMove = (item: HotZoneItemProperty, e: MouseEvent) => { function handleRemove(hotZone: HotZoneItemProperty) {
formData.value = formData.value.filter((item) => item !== hotZone);
}
/** 移动热区 */
function handleMove(item: HotZoneItemProperty, e: MouseEvent) {
useDraggable(item, e, (left, top, _, __, moveWidth, moveHeight) => { useDraggable(item, e, (left, top, _, __, moveWidth, moveHeight) => {
setLeft(item, left + moveWidth); setLeft(item, left + moveWidth);
setTop(item, top + moveHeight); setTop(item, top + moveHeight);
}); });
}; }
// 调整热区大小、位置 /** 调整热区大小、位置 */
const handleResize = ( function handleResize(
item: HotZoneItemProperty, item: HotZoneItemProperty,
ctrlDot: ControlDot, ctrlDot: ControlDot,
e: MouseEvent, e: MouseEvent,
) => { ) {
useDraggable(item, e, (left, top, width, height, moveWidth, moveHeight) => { useDraggable(item, e, (left, top, width, height, moveWidth, moveHeight) => {
ctrlDot.types.forEach((type) => { ctrlDot.types.forEach((type) => {
switch (type) { switch (type) {
@@ -112,23 +116,25 @@ const handleResize = (
} }
}); });
}); });
}; }
// 设置X轴坐标 /** 设置 X 轴坐标 */
const setLeft = (item: HotZoneItemProperty, left: number) => { function setLeft(item: HotZoneItemProperty, left: number) {
// 不能超出容器 // 不能超出容器
if (left >= 0 && left <= container.value!.offsetWidth - item.width) { if (left >= 0 && left <= container.value!.offsetWidth - item.width) {
item.left = left; item.left = left;
} }
}; }
// 设置Y轴坐标
const setTop = (item: HotZoneItemProperty, top: number) => { /** 设置Y轴坐标 */
function setTop(item: HotZoneItemProperty, top: number) {
// 不能超出容器 // 不能超出容器
if (top >= 0 && top <= container.value!.offsetHeight - item.height) { if (top >= 0 && top <= container.value!.offsetHeight - item.height) {
item.top = top; item.top = top;
} }
}; }
// 设置宽度
/** 设置宽度 */
const setWidth = (item: HotZoneItemProperty, width: number) => { const setWidth = (item: HotZoneItemProperty, width: number) => {
// 不能小于最小宽度 && 不能超出容器右边 // 不能小于最小宽度 && 不能超出容器右边
if ( if (
@@ -138,7 +144,8 @@ const setWidth = (item: HotZoneItemProperty, width: number) => {
item.width = width; item.width = width;
} }
}; };
// 设置高度
/** 设置高度 */
const setHeight = (item: HotZoneItemProperty, height: number) => { const setHeight = (item: HotZoneItemProperty, height: number) => {
// 不能小于最小高度 && 不能超出容器底部 // 不能小于最小高度 && 不能超出容器底部
if ( if (
@@ -149,13 +156,12 @@ const setHeight = (item: HotZoneItemProperty, height: number) => {
} }
}; };
// 处理对话框关闭 /** 处理对话框关闭 */
const handleSubmit = () => { const handleSubmit = () => {
// 会自动触发handleClose
dialogVisible.value = false; dialogVisible.value = false;
}; };
// 处理对话框关闭 /** 处理对话框关闭 */
const handleClose = () => { const handleClose = () => {
// 缩小 // 缩小
const list = zoomOut(formData.value); const list = zoomOut(formData.value);
@@ -164,12 +170,16 @@ const handleClose = () => {
const activeHotZone = ref<HotZoneItemProperty>(); const activeHotZone = ref<HotZoneItemProperty>();
const appLinkDialogRef = ref(); const appLinkDialogRef = ref();
const handleShowAppLinkDialog = (hotZone: HotZoneItemProperty) => { const handleShowAppLinkDialog = (hotZone: HotZoneItemProperty) => {
activeHotZone.value = hotZone; activeHotZone.value = hotZone;
appLinkDialogRef.value.open(hotZone.url); appLinkDialogRef.value.open(hotZone.url);
}; };
const handleAppLinkChange = (appLink: AppLink) => { const handleAppLinkChange = (appLink: AppLink) => {
if (!appLink || !activeHotZone.value) return; if (!appLink || !activeHotZone.value) {
return;
}
activeHotZone.value.name = appLink.name; activeHotZone.value.name = appLink.name;
activeHotZone.value.url = appLink.path; activeHotZone.value.url = appLink.path;
}; };
@@ -231,6 +241,7 @@ const handleAppLinkChange = (appLink: AppLink) => {
</ElButton> </ElButton>
</template> </template>
</ElDialog> </ElDialog>
<AppLinkSelectDialog <AppLinkSelectDialog
ref="appLinkDialogRef" ref="appLinkDialogRef"
@app-link-change="handleAppLinkChange" @app-link-change="handleAppLinkChange"

View File

@@ -2,31 +2,22 @@ import type { ComponentStyle, DiyComponent } from '../../../util';
/** 热区属性 */ /** 热区属性 */
export interface HotZoneProperty { export interface HotZoneProperty {
// 图片地址 imgUrl: string; // 图片地址
imgUrl: string; list: HotZoneItemProperty[]; // 导航菜单列表
// 导航菜单列表 style: ComponentStyle; // 组件样式
list: HotZoneItemProperty[];
// 组件样式
style: ComponentStyle;
} }
/** 热区项目属性 */ /** 热区项目属性 */
export interface HotZoneItemProperty { export interface HotZoneItemProperty {
// 链接的名称 name: string; // 链接的名称
name: string; url: string; // 链接
// 链接 width: number; //
url: string; height: number; // 高
// top: number; //
width: number; left: number; // 左
// 高
height: number;
// 上
top: number;
// 左
left: number;
} }
// 定义组件 /** 定义组件 */
export const component = { export const component = {
id: 'HotZone', id: 'HotZone',
name: '热区', name: '热区',

View File

@@ -5,6 +5,7 @@ import { ElImage } from 'element-plus';
/** 热区 */ /** 热区 */
defineOptions({ name: 'HotZone' }); defineOptions({ name: 'HotZone' });
const props = defineProps<{ property: HotZoneProperty }>(); const props = defineProps<{ property: HotZoneProperty }>();
</script> </script>

View File

@@ -15,12 +15,14 @@ import HotZoneEditDialog from './components/hot-zone-edit-dialog/index.vue';
defineOptions({ name: 'HotZoneProperty' }); defineOptions({ name: 'HotZoneProperty' });
const props = defineProps<{ modelValue: HotZoneProperty }>(); const props = defineProps<{ modelValue: HotZoneProperty }>();
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
const formData = useVModel(props, 'modelValue', emit); const formData = useVModel(props, 'modelValue', emit);
// 热区编辑对话框 const editDialogRef = ref(); // 热区编辑对话框
const editDialogRef = ref();
// 打开热区编辑对话框 /** 打开热区编辑对话框 */
const handleOpenEditDialog = () => { const handleOpenEditDialog = () => {
editDialogRef.value.open(); editDialogRef.value.open();
}; };
@@ -49,6 +51,7 @@ const handleOpenEditDialog = () => {
设置热区 设置热区
</ElButton> </ElButton>
</ComponentContainerProperty> </ComponentContainerProperty>
<!-- 热区编辑对话框 --> <!-- 热区编辑对话框 -->
<HotZoneEditDialog <HotZoneEditDialog
ref="editDialogRef" ref="editDialogRef"

View File

@@ -1,3 +1,4 @@
export { default as AppLinkSelectDialog } from './app-link-input/app-link-select-dialog.vue';
export { default as AppLinkInput } from './app-link-input/index.vue'; export { default as AppLinkInput } from './app-link-input/index.vue';
export { default as ColorInput } from './color-input/index.vue'; export { default as ColorInput } from './color-input/index.vue';
export { default as DiyEditor } from './diy-editor/index.vue'; export { default as DiyEditor } from './diy-editor/index.vue';