首页 > 其他分享 >图片热区。vue3+ts和vue3+js写法(js没写完数据,功能完善)

图片热区。vue3+ts和vue3+js写法(js没写完数据,功能完善)

时间:2024-08-08 09:53:50浏览次数:10  
标签:end value js start drag vue3 功能完善 rect const

下面我会将完整的代码放进去,一些样式使用的是全局样式如flex-row,flex-1,size-16,re,tc,等,不过不影响功能使用。

废话不多说,上代码

vue3+ts

<!-- 热区组件 -->
<template>
    <el-dialog v-model="dialog_visible" append-to-body fullscreen @close="close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">编辑热区</div>
            </div>
        </template>
        <el-scrollbar class="content-scrollbar">
            <div class="pa-40 flex-row gap-40">
                <div class="left-content flex-1 pa-20">
                    <el-scrollbar class="img-scrollbar">
                        <div class="img-container">
                            <div ref="imgBoxRef" @mousedown.prevent="start_drag" @mousemove.prevent="move_drag" @mouseup.prevent="end_drag">
                                <el-image :src="hot_list.img" class="w img" @selectstart.prevent @contextmenu.prevent @dragstart.prevent></el-image>
                                <div ref="areaRef" class="area" :style="init_drag_style"></div>
                                <div v-for="(item, index) in hot_list.hot" :key="index" class="area-box" :style="rect_style(item.drag_start, item.drag_end)" @mousedown.prevent="start_drag_area_box(index, $event)" @dblclick="dbl_drag_event(item, index)">
                                    <div class="del-btn" @click.stop="del_area_event(index)"><icon name="close"></icon></div>
                                    <div class="drag-btn" :data-index="index" @mousedown.prevent="start_drag_btn(index, $event)"></div>
                                    <div class="text">
                                        <div class="name">{{ item.name }}</div>
                                        <div class="status" :class="item.status ? 'cr-primary' : 'cr-error'">{{ item.status ? '已设置' : '未设置' }}</div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </el-scrollbar>
                </div>
                <div class="right-content flex-1 pa-20">
                    <div class="size-16 fw mb-10">图片热区</div>
                    <div class="size-12 cr-9 mb-20">框选热区范围,双击设置热区信息</div>
                    <div class="flex-col gap-20 item">
                        <div v-for="(item, index) in hot_list.hot" :key="index" class="flex-row align-c gap-10">
                            <el-input v-model="item.name" class="name" placeholder="名称"></el-input>
                            <url-value v-model="item.link"></url-value>
                            <icon name="del" size="20" @click="del_event(index)"></icon>
                        </div>
                    </div>
                </div>
            </div>
        </el-scrollbar>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" type="primary" @click="confirm_event">完成</el-button>
            </span>
        </template>
    </el-dialog>
    <el-dialog v-model="hot_dialog_visible" width="560" append-to-body @close="hot_close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">设置热区</div>
            </div>
        </template>
        <div class="content">
            <el-form ref="formRef" :model="form" label-width="85px" class="pa-20 mt-16">
                <el-form-item label="热区跳转链接">
                    <url-value v-model="form.link"></url-value>
                </el-form-item>
            </el-form>
        </div>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" @click="hot_close_event">取消</el-button>
                <el-button class="plr-28 ptb-10" type="primary" @click="hot_confirm_event">确定</el-button>
            </span>
        </template>
    </el-dialog>
    <el-button class="w" @click="dialog_visible = true"><icon name="add">编辑热区</icon></el-button>
</template>
<script lang="ts" setup>
import { cloneDeep } from 'lodash';
const app = getCurrentInstance();
/**
 * @description: 热区
 * @param modelValue{Object} 默认值
 * @param dialog_visible {Boolean} 弹窗显示
 * @return {*} update:modelValue
 */
const props = defineProps({});
const modelValue = defineModel({ type: Object as PropType<hotData>, default: {} });
const dialog_visible = defineModel('visibleDialog', { type: Boolean, default: false });
const hot_list = ref<hotData>({
    img: '',
    hot: [],
});
const hot_list_index = ref(0);
watch(
    () => modelValue.value,
    (val) => {
        hot_list.value = cloneDeep(val);
        console.log(val);
    },
    { immediate: true, deep: true }
);

//#region 左侧画布-----------------------------------------------start
const imgBoxRef = ref<HTMLElement | null>(null);
const rect_start = ref<rectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const rect_end = ref<rectCoords>({ x: 0, y: 0, width: 0, height: 0 });
const areaRef = ref<HTMLElement | null>(null);
const init_drag_style = ref('');
const drag_bool = ref(false);
const drag_box_bool = ref(false);
const drag_box_scale_bool = ref(false);
const start_drag = (event: MouseEvent) => {
    drag_bool.value = true;
    if (!imgBoxRef.value) return;
    rect_start.value.x = event.clientX - imgBoxRef.value.getBoundingClientRect().left;
    rect_start.value.y = event.clientY - imgBoxRef.value.getBoundingClientRect().top;
    rect_start.value.width = 0;
    rect_start.value.height = 0;
};
const move_drag = (event: MouseEvent) => {
    if (drag_bool.value) {
        if (!imgBoxRef.value) return;
        rect_end.value.x = event.clientX - imgBoxRef.value.getBoundingClientRect().left;
        rect_end.value.y = event.clientY - imgBoxRef.value.getBoundingClientRect().top;
        rect_end.value.width = rect_end.value.x - rect_start.value.x > 0 ? rect_end.value.x - rect_start.value.x : 0;
        rect_end.value.height = rect_end.value.y - rect_start.value.y > 0 ? rect_end.value.y - rect_start.value.y : 0;
        init_drag_style.value = `left: ${rect_start.value.x}px;top: ${rect_start.value.y}px;width: ${Math.max(rect_end.value.width, 1)}px;height: ${Math.max(rect_end.value.height, 1)}px;display: flex;`;
    }
};
const end_drag = (event: MouseEvent) => {
    drag_bool.value = false;
    if (areaRef.value) areaRef.value.style.display = 'none';
    if (!imgBoxRef.value) return;
    init_drag_style.value = ``;
    if (rect_end.value.width > 16 && rect_end.value.height > 16) {
        hot_list.value.hot.push({
            name: '热区' + (hot_list.value.hot.length + 1),
            link: {},
            drag_start: cloneDeep(rect_start.value),
            drag_end: cloneDeep(rect_end.value),
            status: false,
        });
    }
    rect_end.value = { x: 0, y: 0, width: 0, height: 0 };
};
const area_box_point = ref({ x: 0, y: 0 });
// area-box
const dbl_drag_event = (item: hotListData, index: number) => {
    hot_dialog_visible.value = true;
    form.value.link = item.link;
    hot_list_index.value = index;
};
const start_drag_area_box = (index: number, event: MouseEvent) => {
    hot_list_index.value = index;
    event.stopPropagation();
    drag_box_bool.value = true;
    let clone_drag_start = cloneDeep(hot_list.value.hot[hot_list_index.value].drag_start);
    let clone_drag_end = cloneDeep(hot_list.value.hot[hot_list_index.value].drag_end);
    // 记录原始位置
    area_box_point.value = {
        x: clone_drag_start.x - event.clientX,
        y: clone_drag_start.y - event.clientY,
    };

    // 当子元素拖拽方法触发后夫元素方法不触发
    document.onmousemove = (areaBoxEvent) => {
        areaBoxEvent.stopPropagation();
        if (drag_box_bool.value) {
            if (!imgBoxRef.value) return;
            const new_coordinate = {
                x: areaBoxEvent.clientX + area_box_point.value.x,
                y: areaBoxEvent.clientY + area_box_point.value.y,
            };
            // 左上边界判断
            if (new_coordinate.x < 0) {
                new_coordinate.x = 0;
            }
            if (new_coordinate.y < 0) {
                new_coordinate.y = 0;
            }
            // 右下边界判断
            if (new_coordinate.x + Math.max(clone_drag_end.width, 1) > imgBoxRef.value.getBoundingClientRect().width) {
                new_coordinate.x = imgBoxRef.value.getBoundingClientRect().width - Math.max(clone_drag_end.width, 1);
            }
            if (new_coordinate.y + Math.max(clone_drag_end.height, 1) > imgBoxRef.value.getBoundingClientRect().height) {
                new_coordinate.y = imgBoxRef.value.getBoundingClientRect().height - Math.max(clone_drag_end.height, 1);
            }
            hot_list.value.hot[hot_list_index.value].drag_start.x = new_coordinate.x;
            hot_list.value.hot[hot_list_index.value].drag_start.y = new_coordinate.y;
        }
    };
    document.onmouseup = (areaBoxEvent) => {
        areaBoxEvent.stopPropagation();
        drag_box_bool.value = false;
    };
};
// drag-btn
const start_drag_btn = (index: number, event: MouseEvent) => {
    hot_list_index.value = index;
    event.stopPropagation();
    drag_box_scale_bool.value = true;
    let clone_drag_start = hot_list.value.hot[hot_list_index.value].drag_start;
    let clone_drag_end = hot_list.value.hot[hot_list_index.value].drag_end;
    document.onmousemove = (dragBtnEvent) => {
        dragBtnEvent.stopPropagation();
        //用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
        if (drag_box_scale_bool.value) {
            if (!imgBoxRef.value) return;
            clone_drag_end.x = dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left;
            clone_drag_end.y = dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top;
            hot_list.value.hot[hot_list_index.value].drag_end = {
                x: clone_drag_end.x,
                y: clone_drag_end.y,
                width: clone_drag_end.x - clone_drag_start.x > 0 ? clone_drag_end.x - clone_drag_start.x : 0,
                height: clone_drag_end.y - clone_drag_start.y > 0 ? clone_drag_end.y - clone_drag_start.y : 0,
            };
        }
    };
    document.onmouseup = (dragBtnEvent2) => {
        dragBtnEvent2.stopPropagation();
        drag_box_scale_bool.value = false;
    };
};
const del_area_event = (index: number) => {
    hot_list.value.hot.splice(index, 1);
};
const rect_style = computed(() => {
    return (start: rectCoords, end: rectCoords) => {
        return `left: ${start.x}px;top: ${start.y}px;width: ${Math.max(end.width, 1)}px;height: ${Math.max(end.height, 1)}px;display: flex;`;
    };
});
//#endregion 左侧画布-----------------------------------------------end

//#region 右侧热区编辑-----------------------------------------------start
const del_event = (index: number) => {
    hot_list.value.hot.splice(index, 1);
};
//#endregion 右侧热区编辑-----------------------------------------------end

//#region 设置热区弹窗-----------------------------------------------start
const hot_dialog_visible = ref(false);
const form = ref({
    link: {},
});
const hot_close_event = () => {
    hot_dialog_visible.value = false;
};
const hot_confirm_event = () => {
    hot_list.value.hot[hot_list_index.value].link = form.value.link;
    hot_close_event();
};
//#endregion 设置热区弹窗-----------------------------------------------end

//#region 热区确认取消回调 -----------------------------------------------start
// 取消回调
const close_event = () => {
    dialog_visible.value = false;
};
// 确认回调
const confirm_event = () => {
    modelValue.value = hot_list.value;
    close_event();
};
//#endregion 热区确认取消回调 -----------------------------------------------end
</script>
<style lang="scss" scoped>
.content-scrollbar {
    height: calc(100vh - 13.8rem);
    margin: 0 -1.6rem;
    .left-content {
        .img-scrollbar {
            display: flex;
            justify-content: center;
            .img-container {
                max-width: 60rem;
                min-width: 30rem;
                height: calc(100vh - 25.8rem);
                position: relative;
                .img {
                    user-select: none;
                    cursor: crosshair;
                }
                .area {
                    position: absolute;
                    background: rgba(41, 128, 185, 0.3);
                    border: 1px dashed #34495e;
                    width: 0px;
                    height: 0px;
                    left: 0px;
                    top: 0px;
                    display: none;
                }
                .area-box {
                    position: absolute;
                    background: rgba(42, 148, 255, 0.25);
                    border: 1px dashed #8ec6ff;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    color: #1989fa;
                    font-size: 1.2rem;
                    cursor: move;
                    transition: transform 0.1s;
                    .del-btn {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        background: #1890ff;
                        color: #fff;
                        text-align: center;
                        border-radius: 0 0 0 0.3rem;
                        position: absolute;
                        right: 0.7rem;
                        top: 0.7rem;
                        transform: translate3d(50%, -50%, 0);
                        cursor: default;
                        width: 1.6rem;
                        height: 1.6rem;
                        line-height: 1.6rem;
                        z-index: 1;
                        i {
                            font-size: 0.9rem;
                        }
                    }
                    .drag-btn {
                        position: absolute;
                        width: 7px;
                        height: 7px;
                        background: transparent;
                        right: 0;
                        bottom: 0;
                        transform: translate3d(50%, 50%, 0);
                        cursor: nwse-resize;
                        z-index: 1;
                    }
                    .text {
                        overflow: hidden;
                        display: flex;
                        flex-wrap: wrap;
                        justify-content: center;
                        max-width: 100%;
                        max-height: 100%;
                        text-align: center;
                        align-items: center;
                        color: #fff;
                        font-size: 1.2rem;
                        .name {
                            color: #fff;
                            margin: 0 0.2rem;
                        }
                        .status {
                            margin: 0 0.2rem;
                        }
                    }
                }
            }
        }
    }
    .right-content {
        .item {
            max-width: 47.8rem;
            .name {
                width: 9.8rem;
            }
        }
    }
}
</style>

vue3+js写法

<!-- 上传组件 -->
<template>
    <el-dialog v-model="dialogVisible" width="1168" append-to-body fullscreen @close="close_event">
        <template #header>
            <div class="title re">
                <div class="tc size-16 fw">编辑热区</div>
            </div>
        </template>
        <el-scrollbar class="content-scrollbar">
            <div class="pa-40 flex-row gap-40">
                <div class="left-content flex-1 pa-20">
                    <el-scrollbar class="img-scrollbar">
                        <div class="img-container">
                            <div ref="imgBoxRef" @mousedown.prevent="start_drag" @mousemove.prevent="move_drag" @mouseup.prevent="end_drag">
                                <el-image :src="modelValue.img" class="w img" @selectstart.prevent @contextmenu.prevent @dragstart.prevent></el-image>
                                <div ref="areaRef" class="area" :style="init_drag_style"></div>
                            </div>
                        </div>
                    </el-scrollbar>
                </div>
                <div class="right-content flex-1 pa-20">
                    <div class="size-16 fw mb-10">图片热区</div>
                    <div class="size-12 cr-9 mb-20">框选热区范围,双击设置热区信息</div>
                    <div class="flex-col gap-20 item">
                        <div v-for="(item, index) in modelValue.hot" :key="index" class="flex-row align-c gap-10">
                            <el-input v-model="item.name" class="name" placeholder="名称"></el-input>
                            <url-value v-model="item.link"></url-value>
                            <icon name="del" size="20" @click="del_event(index)"></icon>
                        </div>
                    </div>
                </div>
            </div>
        </el-scrollbar>
        <template #footer>
            <span class="dialog-footer">
                <el-button class="plr-28 ptb-10" type="primary" @click="confirm_event">完成</el-button>
            </span>
        </template>
    </el-dialog>
    <el-button class="w" @click="dialogVisible = true"><icon name="add">编辑热区</icon></el-button>
</template>
<script lang="ts" setup>
import { cloneDeep } from 'lodash';
const app = getCurrentInstance();
/**
 * @description: 热区
 * @param modelValue{Object} 默认值
 * @param dialogVisible {Boolean} 弹窗显示
 * @param type{String} 链接类型为空数组则表示无限制,全部可用,传过来则表示传的值可用
 * @param placeholder{String} 提示文字
 * @return {*} update:modelValue
 */
const props = defineProps({});
const modelValue = defineModel({ type: Object as PropType<hotData>, default: {} });
const dialogVisible = defineModel('visibleDialog', { type: Boolean, default: false });

//#region 左侧画布-----------------------------------------------start
interface RectCoords {
    x: number;
    y: number;
    width: number;
    height: number;
}

interface drag {
    status: boolean;
    name: string;
    link: object;
}
const drag_list = ref<drag[]>([]);

const imgBoxRef = ref<HTMLElement | null>(null);
// 开始坐标
const rect_start = ref<RectCoords>({ x: 0, y: 0, width: 0, height: 0 });
// 结束坐标
const rect_end = ref<RectCoords>({ x: 0, y: 0, width: 0, height: 0 });
// 拖拽显示的占位区域
const areaRef = ref<HTMLElement | null>(null);
// 拖拽显示的初始化样式
const init_drag_style = ref('');
// 拖拽开关
const drag_bool = ref(false);
// 拖拽box开关
const drag_box_bool = ref(false);
// 拖拽box放大缩小开关
const drag_box_scale_bool = ref(false);
// 拖拽鼠标左击 开始
const start_drag = (event: MouseEvent) => {
    drag_bool.value = true;
    if (!imgBoxRef.value) return;
    rect_start.value.x = event.clientX - imgBoxRef.value.getBoundingClientRect().left;
    rect_start.value.y = event.clientY - imgBoxRef.value.getBoundingClientRect().top;
    rect_start.value.width = 0;
    rect_start.value.height = 0;
};
// 跟随鼠标移动
const move_drag = (event: MouseEvent) => {
    if (drag_bool.value) {
        if (!imgBoxRef.value) return;
        rect_end.value.x = event.clientX - imgBoxRef.value.getBoundingClientRect().left;
        rect_end.value.y = event.clientY - imgBoxRef.value.getBoundingClientRect().top;
        rect_end.value.width = rect_end.value.x - rect_start.value.x > 0 ? rect_end.value.x - rect_start.value.x : 0;
        rect_end.value.height = rect_end.value.y - rect_start.value.y > 0 ? rect_end.value.y - rect_start.value.y : 0;
        init_drag_style.value = rect_style(rect_start.value, rect_end.value);
    }
};
// 拖拽结束
const end_drag = (event: MouseEvent) => {
    drag_bool.value = false;
    if (areaRef.value) areaRef.value.style.display = 'none';
    if (!imgBoxRef.value) return;
    let clone_drag_start = cloneDeep(rect_start.value);
    let clone_drag_end = cloneDeep(rect_end.value);
    init_drag_style.value = ``;
    if (rect_end.value.width > 16 && rect_end.value.height > 16) {
        // 克隆area元素,并将class=“area”改为area-box
        const area_box = document.createElement('div');
        area_box.className = 'area-box';
        area_box.style.cssText = rect_style(clone_drag_start, clone_drag_end);
        if (areaRef.value) areaRef.value.parentNode?.appendChild(area_box);
        drag_list.value.push({
            name: '热区' + drag_list.value.length + 1,
            link: {},
            status: false,
        });
        // area_box 添加拖拽功能
        area_box.onmousedown = (areaEvent) => {
            areaEvent.stopPropagation();
            drag_box_bool.value = true;
            // 记录原始位置
            let area_box_point = {
                x: clone_drag_start.x - areaEvent.clientX,
                y: clone_drag_start.y - areaEvent.clientY,
            };
            // 当子元素拖拽方法触发后夫元素方法不触发
            document.onmousemove = (areaBoxEvent) => {
                areaBoxEvent.stopPropagation();
                if (drag_box_bool.value) {
                    console.log('area_box onm ousemove');
                    if (!imgBoxRef.value) return;
                    const new_coordinate = {
                        x: areaBoxEvent.clientX + area_box_point.x,
                        y: areaBoxEvent.clientY + area_box_point.y,
                    };
                    // 左上边界判断
                    if (new_coordinate.x < 0) {
                        new_coordinate.x = 0;
                    }
                    if (new_coordinate.y < 0) {
                        new_coordinate.y = 0;
                    }
                    // 右下边界判断
                    if (new_coordinate.x + Math.max(clone_drag_end.width, 1) > imgBoxRef.value.getBoundingClientRect().width) {
                        new_coordinate.x = imgBoxRef.value.getBoundingClientRect().width - Math.max(clone_drag_end.width, 1);
                    }
                    if (new_coordinate.y + Math.max(clone_drag_end.height, 1) > imgBoxRef.value.getBoundingClientRect().height) {
                        new_coordinate.y = imgBoxRef.value.getBoundingClientRect().height - Math.max(clone_drag_end.height, 1);
                    }
                    clone_drag_start.x = new_coordinate.x;
                    clone_drag_start.y = new_coordinate.y;
                    area_box.style.cssText = rect_style(clone_drag_start, clone_drag_end);
                }
            };
            document.onmouseup = (areaBoxEvent) => {
                areaBoxEvent.stopPropagation();
                drag_box_bool.value = false;
            };
        };
        // 在新增的元素内创建删除按钮 ---------------
        const del_btn = document.createElement('div');
        del_btn.className = 'del-btn';
        del_btn.innerHTML = `<i class="iconfont icon-close"></i>`;
        area_box.appendChild(del_btn);
        del_btn.onclick = () => {
            event.stopPropagation();
            // 将当前点击的del_btn的父级area_box完全删除不留痕迹
            area_box.parentNode?.removeChild(area_box);
        };
        // 在新增的元素内创建拖拽按钮 ---------------
        const drag_btn = document.createElement('div');
        drag_btn.className = 'drag-btn';
        area_box.appendChild(drag_btn);
        // 当子元素拖拽方法触发后夫元素方法不触发
        drag_btn.onmousedown = (dragBtnEvent) => {
            dragBtnEvent.stopPropagation();
            drag_box_scale_bool.value = true;
            // // 使用document绑定的drag事件:如果绑定到元素本身的情况下,鼠标拖动过快,鼠标会离开拖拽的元素,导致拖拽一段距离,拖拽失效的问题
            document.onmousemove = (dragBtnEvent2) => {
                dragBtnEvent2.stopPropagation();
                //用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
                if (drag_box_scale_bool.value) {
                    if (!imgBoxRef.value) return;
                    clone_drag_end.x = dragBtnEvent2.clientX - imgBoxRef.value.getBoundingClientRect().left;
                    clone_drag_end.y = dragBtnEvent2.clientY - imgBoxRef.value.getBoundingClientRect().top;
                    clone_drag_end.width = clone_drag_end.x - clone_drag_start.x > 0 ? clone_drag_end.x - clone_drag_start.x : 0;
                    clone_drag_end.height = clone_drag_end.y - clone_drag_start.y > 0 ? clone_drag_end.y - clone_drag_start.y : 0;
                    area_box.style.cssText = rect_style(clone_drag_start, clone_drag_end);
                }
            };
            document.onmouseup = (dragBtnEvent3) => {
                dragBtnEvent3.stopPropagation();
                drag_box_scale_bool.value = false;
            };
        };
        // 在新增的元素内创建文本提示---------------
        const drag_text = document.createElement('div');
        drag_text.className = 'text';
        drag_text.innerHTML = `<div class="name">热区一</div><div class="status">未设置</div>`;
        area_box.appendChild(drag_text);
    }
    rect_end.value = { x: 0, y: 0, width: 0, height: 0 };
};
const rect_style = (start: RectCoords, end: RectCoords) => {
    return `left: ${start.x}px;top: ${start.y}px;width: ${Math.max(end.width, 1)}px;height: ${Math.max(end.height, 1)}px;display: flex;`;
};
//#region 左侧画布-----------------------------------------------end

//#region 右侧热区编辑-----------------------------------------------start
const del_event = (index: number) => {
    modelValue.value.hot.splice(index, 1);
};
//#region 右侧热区编辑-----------------------------------------------end

//#region 热区确认取消回调 -----------------------------------------------start
// 取消回调
const close_event = () => {
    dialogVisible.value = false;
};
// 确认回调
const confirm_event = () => {
    close_event();
};
//#endregion 热区确认取消回调 -----------------------------------------------end
</script>
<style lang="scss" scoped>
.content-scrollbar {
    height: calc(100vh - 13.8rem);
    margin: 0 -1.6rem;
    .left-content {
        .img-scrollbar {
            display: flex;
            justify-content: center;
            .img-container {
                max-width: 60rem;
                min-width: 30rem;
                height: calc(100vh - 25.8rem);
                position: relative;
                .img {
                    user-select: none;
                    cursor: crosshair;
                }
                .area {
                    position: absolute;
                    background: rgba(41, 128, 185, 0.3);
                    border: 1px dashed #34495e;
                    width: 0px;
                    height: 0px;
                    left: 0px;
                    top: 0px;
                    display: none;
                }
                :deep(.area-box) {
                    position: absolute;
                    background: rgba(42, 148, 255, 0.25);
                    border: 1px dashed #8ec6ff;
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    color: #1989fa;
                    font-size: 1.2rem;
                    cursor: move;
                    transition: transform 0.1s;
                    .del-btn {
                        display: flex;
                        justify-content: center;
                        align-items: center;
                        background: #1890ff;
                        color: #fff;
                        text-align: center;
                        border-radius: 0 0 0 0.3rem;
                        position: absolute;
                        right: 0.7rem;
                        top: 0.7rem;
                        transform: translate3d(50%, -50%, 0);
                        cursor: default;
                        width: 1.6rem;
                        height: 1.6rem;
                        line-height: 1.6rem;
                        z-index: 1;
                        i {
                            font-size: 0.9rem;
                        }
                    }
                    .drag-btn {
                        position: absolute;
                        width: 7px;
                        height: 7px;
                        background: transparent;
                        right: 0;
                        bottom: 0;
                        transform: translate3d(50%, 50%, 0);
                        cursor: nwse-resize;
                        z-index: 1;
                    }
                    .text {
                        overflow: hidden;
                        display: flex;
                        flex-wrap: wrap;
                        justify-content: center;
                        max-width: 100%;
                        max-height: 100%;
                        text-align: center;
                        align-items: center;
                        color: #fff;
                        font-size: 1.2rem;
                        .name,
                        .status {
                            color: #fff;
                            margin: 0 2px;
                        }
                    }
                }
            }
        }
    }
    .right-content {
        .item {
            max-width: 47.8rem;
            .name {
                width: 9.8rem;
            }
        }
    }
}
</style>

预览效果:

 

标签:end,value,js,start,drag,vue3,功能完善,rect,const
From: https://www.cnblogs.com/sws-kevin/p/18348331

相关文章

  • JsonConvert中处理Null值问题
    1.定义一个类 NullToEmptyStringConverter 继承 JsonConverterusingNewtonsoft.Json;usingNewtonsoft.Json.Linq;usingSystem;publicclassNullToEmptyStringConverter:JsonConverter{publicoverrideboolCanConvert(TypeobjectType){returntrue;......
  • 计算机毕业设计项目推荐,院系资料分类管理平台 84184(开题答辩+程序定制+全套文案 )上万
    目 录摘要1绪论1.1研究背景1.2研究意义1.3论文结构与章节安排2 院系资料分类管理平台系统分析2.1可行性分析2.2系统流程分析2.2.1数据增加流程2.2.2数据修改流程2.2.3数据删除流程2.3系统功能分析2.3.1功能性分析2.3.2非功能性分析......
  • 计算机毕业设计项目推荐,红色旅游网站设计与开发 99214(开题答辩+程序定制+全套文案 )上
    摘 要21世纪时信息化的时代,几乎任何一个行业都离不开计算机,将计算机运用于旅游服务管理也是十分常见的。过去使用手工的管理方式对旅游服务进行管理,造成了管理繁琐、难以维护等问题,如今使用计算机对旅游服务的各项基本信息进行管理,比起手工管理来说既方便又简单,而且具有易......
  • Electron + Vue+Node.js 搭建前端桌面应用
    一、在使用Electron之前我们要了解Electron是什么?Electron官网地址点此: electron官方地址Electron相当于一个浏览器的外壳,我们将编写的HTML,CSS,Javascript网页程序嵌入进Electron里面以便于在桌面上进行运行。通俗来讲它就是一个软件,如QQ、网易......
  • 如何使用 FastAPI 返回 JSON 格式的 csv 文件/Pandas DataFrame?
    我有一个.csv文件,我想在FastAPI应用程序中渲染。我只设法以JSON格式呈现.csv文件,如下所示:deftransform_question_format(csv_file_name):json_file_name=f"{csv_file_name[:-4]}.json"#transformsthecsvfileintojsonfilepd.r......
  • 基于nodejs+vue教育企业网站[程序+论文+开题]-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着互联网技术的飞速发展,教育领域正经历着前所未有的变革。传统教育模式逐渐向线上线下融合的方向转型,教育企业作为这一变革的重要推动者,亟需构建高效、便......
  • 基于nodejs+vue结合疫情情况的婚恋系统[程序+论文+开题]-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景近年来,全球范围内的新冠疫情不仅深刻改变了人们的生活方式,也对传统婚恋观念与模式产生了深远影响。疫情期间,社交活动受限,面对面交流的机会大幅减少,使得单身......
  • 基于nodejs+vue金珠当铺[程序+论文+开题]-计算机毕业设计
    本系统(程序+源码+数据库+调试部署+开发环境)带文档lw万字以上,文末可获取源码系统程序文件列表开题报告内容研究背景随着现代社会经济的快速发展,人们的金融需求日益多样化,传统金融服务已难以满足所有层次的客户需求。特别是在应急资金周转、贵重物品保管及短期融资方面,市场......
  • JSOI2024 游记
    UPD:其实是早就写好了的,不过现在差不多想发了。JSOI2024游记Day?~Day-1省选前的心态算是比较失落,每次点人都发现自己进不了一点。不过今年还是得打,尽力而为吧。比赛前一周基本补完了之前落下的联考,打的还算可以。湖北省选模拟,打了个Day2,非常没有水平的100+100+43,感......
  • 牛客JS题(二十四)验证是否是身份证
    注释很详细,直接上代码涉及知识点:正则表达式一代与二代身份证判断题干:我的答案<!DOCTYPEhtml><html><head><metacharset="UTF-8"/><style>/*填写样式*/</style></head><body><!--填写标签-......