废话不多说,上代码

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);
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 = 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 onmousemove');
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 = () => {
// modelValue.value = link_value.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;
}
: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>

预览效果:

图片热区。vue3+ts和vue3+js写法(js没写完数据,功能完善)的更多相关文章

  1. 学习 vue3 第一天 vue3简介,创建vue3项目 Composition Api 初识

    前言: 从今天开始来和大家一起学习 vue3 相信大家都不陌生,已经火了一段时间了,但是还是有不少人没有学习,那就跟着六扇老师来简单的入个门 废话不多说,来开始今天的学习 Vue3 简介: 2020年 ...

  2. 【electron+vue3+ts实战便笺exe】一、搭建框架配置

    不要让自己的上限成为你的底线 前言 诈尸更新系列,为了跟上脚步,尝试了vue3,在学习vue3的时候顺便学习一手electron和ts,本教程将分别发布,源码会在最后的文章发布.因为还在开发中,目前也 ...

  3. 基于Vue3+TS的Monorepo前端项目架构设计与实现

    写在前面 你好,我是前端程序员鼓励师岩家兴!去年在另一个项目https://juejin.cn/post/7121736546000044046中,我向读者朋友们介绍了结合npm包管理工具yarn作v ...

  4. 【electron+vue3+ts实战便笺exe】二、electron+vue3开发内容

    不要让自己的上限成为你的底线 本来以为有万字的..没想到才堪堪近6000字.为了水文的嫌疑,只挑了重点的地方讲,比如component内的组件就挑了右键弹窗去说明,建议在看本文的时候边查看项目,有不懂 ...

  5. Vite2+Vue3+ts的eslint设置踩坑

    目录 新项目了 Vite搭建 eslint 先安装eslint 创建.eslintrc.js 引入规则 Airbnb 配合prettier 对ts的支持 .eslintrc.js 在页面上查看esli ...

  6. vite创建vue3+ts项目流程

    vite+vue3+typescript搭建项目过程   vite和vue3.0都出来一段时间了,尝试一下搭vite+vue3+ts的项目 相关资料网址 vue3.0官网:https://v3.vue ...

  7. 使用Vite快速构建Vue3+ts+pinia脚手架

    一.前言 vue3的快速更新,很多IT发展快的地区在22开始都已经提上日程,小编所在的青岛好像最近才有点风波.vue3的人才在青岛还是比较稀缺的哈,纯属小编自己的看法,可能小编是个井底之蛙!! vue ...

  8. vue3+TS 自定义指令:长按触发绑定的函数

    vue3+TS 自定义指令:长按触发绑定的函数 而然间看到一个在vue2中写的长按触发事件的自定义指定,想着能不能把他copy到我的vue3项目中呢. 编写自定义指令时遇到的几个难点 1.自定义指令的 ...

  9. vue3+ts获取dom元素高度

    vue3+ts获取dom元素高度 <template> <div class="digestDetail-indedx"> <div class=&q ...

  10. [jquery] 图片热区随图片大小自由缩放

    在图片上直接画出带超级链接热区元素map和area相信大家并不陌生,Dreamweaver等网页制作软件都有直接在图片上绘制带超级链接的热区工具,但是直接绘制的热区是不能随着图片自适应放大和缩小的,现 ...

随机推荐

  1. 记Codes 重新定义 SaaS模式开源免费研发项目管理平台——多事项闭环迭代的创新实现

    1.简介 Codes 重新定义 SaaS 模式 = 云端认证 + 程序及数据本地安装 + 不限功能 + 30 人免费 Codes 是一个 高效.简洁.轻量的一站式研发项目管理平台.包含需求管理,任务管 ...

  2. CSV文件存储

    CSV 文件存储 CSV,全称为 Comma-Separated Values,中文可以叫作逗号分隔值或字符分隔值,其文件以纯文本形式存储表格数据.该文件是一个字符序列,可以由任意数目的记录组成,记录 ...

  3. typroa破解

    Typora 一款 Markdown 编辑器和阅读器 风格极简 / 多种主题 / 支持 macOS,Windows 及 Linux 实时预览 / 图片与文字 / 代码块 / 数学公式 / 图表 目录大 ...

  4. 基恩士PLC数据 转 Modbus RTU TCP项目案例

    1         案例说明 1.   设置网关采集基恩士PLC数据 2.   把采集的数据转成Modbus协议转发给其他系统. var code = "244226f8-1eed-48e4 ...

  5. 全国产!瑞芯微RK3568J/RK3568B2工业核心板规格书

    核心板简介 创龙科技SOM-TL3568是一款基于瑞芯微RK3568J/RK3568B2处理器设计的四核ARM Cortex-A55全国产工业核心板,每核主频高达1.8GHz/2.0GHz.核心板CP ...

  6. 基于vsftpd搭建项目文件服务器

    vsftpd 是"very secure FTP daemon"的缩写,安全性是它的一个最大的特点.vsftpd 是一个 UNIX 类操作系统上运行的服务器的名字,它可以运行在诸如 ...

  7. 常用IDE(开发工具)

    一.开发工具 Visual Studio Microsoft Visual Studio(简称VS)是微软公司提供的IDE,可以在VS上编写C.C++.C#等多种语言的项目,所写的代码适用于微软支持的 ...

  8. 题解:P10672 【MX-S1-T1】壁垒

    暑期集训=依托答辩. 分析 种类数是奇数一定无解. 否则每种数字先输出一次,在此过程中每增加两个数时,因为每个数字种类数都不一样,所以前缀种类数也同时增加 \(2\),保证一定为偶数. 然后输出完以后 ...

  9. 关于Pure中使用RnadomInteger问题的BUG

    每一次连线都会造成返回值不一样相当于重新调用了一次,所以返回值需要新建变量存储来使用而不是直接用它拉两次线 下面是错误写法: 正确写法:

  10. c#写一个WINFORM的多线程操作

    以下是一个简单的示例,展示了如何在C# WinForms中创建一个按钮的异步事件,并使用Label控件来显示事件执行的时长. 首先,确保你已经在你的项目中添加了一个Button和一个Label控件.假 ...