1.热区新增6个可拖拽的角
2.热区新增不可拖拽出去 3.已设置改为链接名称 4.热区数据增加类型约束 5.热区弹窗在打开后图片内的坐标会根据当前浏览器的大小进行动态调整 6.优化了热区回显没有完全对准的问题 7.热区图片大小从最大60rem更新为最大80rem 8.上传分类增加新增编辑删除v1.0.0
parent
b7c5d87691
commit
d6a6b8a71f
|
|
@ -0,0 +1,138 @@
|
|||
.content-scrollbar {
|
||||
height: calc(100vh - 13.8rem);
|
||||
margin: 0 -1.6rem;
|
||||
.left-content {
|
||||
.img-scrollbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
.img-container {
|
||||
max-width: 80rem;
|
||||
min-width: 30rem;
|
||||
height: calc(100vh - 25.8rem);
|
||||
position: relative;
|
||||
padding: 0.8rem;
|
||||
.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: #f0f0f0;
|
||||
border: 1px solid #333;
|
||||
z-index: 1;
|
||||
}
|
||||
.drag-tl {
|
||||
left: -0.4rem;
|
||||
top: -0.4rem;
|
||||
cursor: nw-resize;
|
||||
}
|
||||
.drag-tc {
|
||||
left: 50%;
|
||||
top: -0.4rem;
|
||||
transform: translateX(-50%);
|
||||
cursor: n-resize;
|
||||
}
|
||||
.drag-lc {
|
||||
left: -0.4rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: w-resize;
|
||||
}
|
||||
.drag-bl {
|
||||
left: -0.4rem;
|
||||
bottom: -0.4rem;
|
||||
cursor: sw-resize;
|
||||
}
|
||||
.drag-bc {
|
||||
left: 50%;
|
||||
bottom: -0.4rem;
|
||||
transform: translateX(-50%);
|
||||
cursor: s-resize;
|
||||
}
|
||||
.drag-br {
|
||||
right: -0.4rem;
|
||||
bottom: -0.4rem;
|
||||
cursor: se-resize;
|
||||
}
|
||||
.drag-rc {
|
||||
right: -0.4rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: e-resize;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,17 +11,26 @@
|
|||
<div class="left-content flex-1 pa-20">
|
||||
<el-scrollbar class="img-scrollbar">
|
||||
<div class="img-container">
|
||||
<div ref="imgBoxRef" class="oh" @mousedown.prevent="start_drag" @mousemove.prevent="move_drag" @mouseup.prevent="end_drag">
|
||||
<div ref="imgRef">
|
||||
<el-image :src="hot_list.img" class="w img" @selectstart.prevent @contextmenu.prevent @dragstart.prevent></el-image>
|
||||
</div>
|
||||
<div ref="areaRef" class="area" :style="init_drag_style"></div>
|
||||
<div v-for="(item, index) in hot_list.data" :key="index" class="area-box" :style="rect_style(item.drag_start, item.drag_end)" @mousedown.stop="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.stop="start_drag_btn(index, $event)"></div>
|
||||
<div class="text">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="status" :class="!is_obj_empty(item.link) ? 'cr-primary' : 'cr-error'">{{ !is_obj_empty(item.link) ? '已设置' : '未设置' }}</div>
|
||||
<div class="re">
|
||||
<div ref="imgBoxRef" class="oh" @mousedown.prevent="start_drag" @mousemove.prevent="move_drag" @mouseup.prevent="end_drag">
|
||||
<div ref="imgRef">
|
||||
<el-image :src="hot_list.img" class="w img block" @selectstart.prevent @contextmenu.prevent @dragstart.prevent></el-image>
|
||||
</div>
|
||||
<div ref="areaRef" class="area" :style="init_drag_style"></div>
|
||||
<div v-for="(item, index) in hot_list.data" :key="index" class="area-box" :style="rect_style(item.drag_start, item.drag_end)" @mousedown.stop="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 drag-tl" :data-index="index" @mousedown.stop="start_drag_btn_tl(index, $event)"></div>
|
||||
<div class="drag-btn drag-tc" :data-index="index" @mousedown.stop="start_drag_btn_tc(index, $event)"></div>
|
||||
<div class="drag-btn drag-lc" :data-index="index" @mousedown.stop="start_drag_btn_lc(index, $event)"></div>
|
||||
<div class="drag-btn drag-bl" :data-index="index" @mousedown.stop="start_drag_btn_bl(index, $event)"></div>
|
||||
<div class="drag-btn drag-bc" :data-index="index" @mousedown.stop="start_drag_btn_bc(index, $event)"></div>
|
||||
<!-- 已完成 -->
|
||||
<div class="drag-btn drag-br" :data-index="index" @mousedown.stop="start_drag_btn_br(index, $event)"></div>
|
||||
<div class="drag-btn drag-rc" :data-index="index" @mousedown.stop="start_drag_btn_rc(index, $event)"></div>
|
||||
<div class="text">
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="status" :class="!is_obj_empty(item.link) ? 'cr-primary' : 'cr-error'">{{ !is_obj_empty(item.link) ? (item.link?.name ?? '未设置') : '未设置' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -101,8 +110,11 @@ 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;
|
||||
|
|
@ -130,7 +142,7 @@ const end_drag = (event: MouseEvent) => {
|
|||
if (rect_end.value.width > 16 && rect_end.value.height > 16) {
|
||||
hot_list.value.data.push({
|
||||
name: '热区' + (hot_list.value.data.length + 1),
|
||||
link: {},
|
||||
link: { name: '', link: '' },
|
||||
drag_start: cloneDeep(rect_start.value),
|
||||
drag_end: cloneDeep(rect_end.value),
|
||||
});
|
||||
|
|
@ -176,13 +188,15 @@ const start_drag_area_box = (index: number, event: MouseEvent) => {
|
|||
}
|
||||
// 右下边界判断
|
||||
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) - 4;
|
||||
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) - 7;
|
||||
new_coordinate.y = imgBoxRef.value.getBoundingClientRect().height - Math.max(clone_drag_end.height, 1);
|
||||
}
|
||||
hot_list.value.data[hot_list_index.value].drag_start.x = new_coordinate.x;
|
||||
hot_list.value.data[hot_list_index.value].drag_start.y = new_coordinate.y;
|
||||
hot_list.value.data[hot_list_index.value].drag_end.x = new_coordinate.x + Math.max(clone_drag_end.width, 1);
|
||||
hot_list.value.data[hot_list_index.value].drag_end.y = new_coordinate.y + Math.max(clone_drag_end.height, 1);
|
||||
}
|
||||
};
|
||||
document.onmouseup = (areaBoxEvent) => {
|
||||
|
|
@ -191,7 +205,29 @@ const start_drag_area_box = (index: number, event: MouseEvent) => {
|
|||
};
|
||||
};
|
||||
// drag-btn
|
||||
const start_drag_btn = (index: number, event: MouseEvent) => {
|
||||
const start_drag_btn_br = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'br');
|
||||
};
|
||||
const start_drag_btn_bl = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'bl');
|
||||
};
|
||||
const start_drag_btn_bc = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'bc');
|
||||
};
|
||||
const start_drag_btn_tl = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'tl');
|
||||
};
|
||||
const start_drag_btn_tc = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'tc');
|
||||
};
|
||||
const start_drag_btn_lc = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'lc');
|
||||
};
|
||||
const start_drag_btn_rc = (index: number, event: MouseEvent) => {
|
||||
start_drag_btn(index, event, 'rc');
|
||||
};
|
||||
// 画布拖拽公用方法
|
||||
const start_drag_btn = (index: number, event: MouseEvent, type: string) => {
|
||||
hot_list_index.value = index;
|
||||
event.stopPropagation();
|
||||
drag_box_scale_bool.value = true;
|
||||
|
|
@ -202,14 +238,55 @@ const start_drag_btn = (index: number, event: MouseEvent) => {
|
|||
//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
|
||||
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.data[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,
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case 'br':
|
||||
// 下右
|
||||
clone_drag_end.x = handleBoundary(dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left, 0, imgBoxRef.value.getBoundingClientRect().width);
|
||||
clone_drag_end.y = handleBoundary(dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top, 0, imgBoxRef.value.getBoundingClientRect().height);
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, clone_drag_end);
|
||||
break;
|
||||
case 'bl':
|
||||
// 下左
|
||||
clone_drag_start.x = handleBoundary(dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left, 0, clone_drag_end.x);
|
||||
clone_drag_end.y = handleBoundary(dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top, 0, imgBoxRef.value.getBoundingClientRect().height);
|
||||
hot_list.value.data[hot_list_index.value].drag_start.x = clone_drag_start.x;
|
||||
hot_list.value.data[hot_list_index.value].drag_end.y = clone_drag_end.y;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, { y: clone_drag_end.y });
|
||||
break;
|
||||
case 'bc':
|
||||
// 下中
|
||||
clone_drag_end.y = handleBoundary(dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top, 0, imgBoxRef.value.getBoundingClientRect().height);
|
||||
hot_list.value.data[hot_list_index.value].drag_end.y = clone_drag_end.y;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, { y: clone_drag_end.y });
|
||||
break;
|
||||
case 'tl':
|
||||
// 上左
|
||||
clone_drag_start.x = handleBoundary(dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left, 0, clone_drag_end.x);
|
||||
clone_drag_start.y = handleBoundary(dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top, 0, clone_drag_end.y);
|
||||
hot_list.value.data[hot_list_index.value].drag_start.x = clone_drag_start.x;
|
||||
hot_list.value.data[hot_list_index.value].drag_start.y = clone_drag_start.y;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, {});
|
||||
break;
|
||||
case 'tc':
|
||||
// 上中
|
||||
clone_drag_start.y = handleBoundary(dragBtnEvent.clientY - imgBoxRef.value.getBoundingClientRect().top, 0, clone_drag_end.y);
|
||||
hot_list.value.data[hot_list_index.value].drag_start.y = clone_drag_start.y;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, { y: clone_drag_end.y });
|
||||
break;
|
||||
case 'lc':
|
||||
// 左中
|
||||
clone_drag_start.x = handleBoundary(dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left, 0, clone_drag_end.x);
|
||||
hot_list.value.data[hot_list_index.value].drag_start.x = clone_drag_start.x;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, {});
|
||||
break;
|
||||
case 'rc':
|
||||
// 右中
|
||||
clone_drag_end.x = handleBoundary(dragBtnEvent.clientX - imgBoxRef.value.getBoundingClientRect().left, 0, imgBoxRef.value.getBoundingClientRect().width);
|
||||
hot_list.value.data[hot_list_index.value].drag_end.x = clone_drag_end.x;
|
||||
hot_list.value.data[hot_list_index.value].drag_end = updateDragEnd(clone_drag_start, clone_drag_end, { x: clone_drag_end.x });
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
document.onmouseup = (dragBtnEvent2) => {
|
||||
|
|
@ -217,6 +294,29 @@ const start_drag_btn = (index: number, event: MouseEvent) => {
|
|||
drag_box_scale_bool.value = false;
|
||||
};
|
||||
};
|
||||
|
||||
// 辅助函数用于更新drag_end
|
||||
const updateDragEnd = (dragStart: { x: number; y: number }, dragEnd: { x: number; y: number }, newDragEnd: { x?: number; y?: number }) => {
|
||||
const newX = newDragEnd.x !== undefined ? newDragEnd.x : dragEnd.x;
|
||||
const newY = newDragEnd.y !== undefined ? newDragEnd.y : dragEnd.y;
|
||||
return {
|
||||
x: newX,
|
||||
y: newY,
|
||||
width: newX - dragStart.x > 0 ? newX - dragStart.x : 0,
|
||||
height: newY - dragStart.y > 0 ? newY - dragStart.y : 0,
|
||||
};
|
||||
};
|
||||
|
||||
// 辅助函数用于更新drag_start
|
||||
const updateDragStart = (dragStart: { x: number; y: number }, newDragStart: { x?: number; y?: number }) => {
|
||||
const newX = newDragStart.x !== undefined ? newDragStart.x : dragStart.x;
|
||||
const newY = newDragStart.y !== undefined ? newDragStart.y : dragStart.y;
|
||||
return { x: newX, y: newY };
|
||||
};
|
||||
|
||||
// 辅助函数用于处理边界
|
||||
const handleBoundary = (value: number, min: number, max: number) => Math.max(min, Math.min(value, max));
|
||||
|
||||
const del_area_event = (index: number) => {
|
||||
hot_list.value.data.splice(index, 1);
|
||||
};
|
||||
|
|
@ -225,6 +325,7 @@ const rect_style = computed(() => {
|
|||
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
|
||||
|
|
@ -235,16 +336,24 @@ const del_event = (index: number) => {
|
|||
|
||||
//#region 设置热区弹窗-----------------------------------------------start
|
||||
const hot_dialog_visible = ref(false);
|
||||
const form = ref({
|
||||
link: {},
|
||||
interface formData {
|
||||
link: linkData;
|
||||
name: string;
|
||||
}
|
||||
const form = ref<formData>({
|
||||
link: {
|
||||
name: '',
|
||||
},
|
||||
name: '',
|
||||
});
|
||||
const hot_close_event = () => {
|
||||
hot_dialog_visible.value = false;
|
||||
};
|
||||
const hot_confirm_event = () => {
|
||||
hot_list.value.data[hot_list_index.value].link = form.value.link;
|
||||
hot_list.value.data[hot_list_index.value].name = form.value.name;
|
||||
if (hot_list.value.data[hot_list_index.value].link) {
|
||||
hot_list.value.data[hot_list_index.value].link = form.value.link;
|
||||
}
|
||||
hot_close_event();
|
||||
};
|
||||
//#endregion 设置热区弹窗-----------------------------------------------end
|
||||
|
|
@ -254,7 +363,26 @@ const hot_confirm_event = () => {
|
|||
const open_hot_event = () => {
|
||||
if (modelValue.value.img.length > 0) {
|
||||
dialog_visible.value = true;
|
||||
hot_list.value = cloneDeep(modelValue.value);
|
||||
hot_list.value.img = modelValue.value.img;
|
||||
setTimeout(() => {
|
||||
// 创建临时变量储存传过来的数据
|
||||
let temp_data = cloneDeep(modelValue.value);
|
||||
// 获取最新的图片高度和宽度
|
||||
temp_data.img_height = imgBoxRef.value?.clientHeight || 0;
|
||||
temp_data.img_width = imgBoxRef.value?.clientWidth || 0;
|
||||
// 根据原始数据的宽高和更新后的宽高的比例,计算出事实的坐标比例
|
||||
const scale = temp_data.img_width / modelValue.value.img_width;
|
||||
console.log(scale);
|
||||
temp_data.data.forEach((item) => {
|
||||
item.drag_start.x = item.drag_start.x * scale;
|
||||
item.drag_start.y = item.drag_start.y * scale;
|
||||
item.drag_end.x = item.drag_end.x * scale;
|
||||
item.drag_end.y = item.drag_end.y * scale;
|
||||
item.drag_end.width = item.drag_end.width * scale;
|
||||
item.drag_end.height = item.drag_end.height * scale;
|
||||
});
|
||||
hot_list.value = temp_data;
|
||||
}, 100);
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
|
|
@ -285,8 +413,6 @@ const confirm_event = () => {
|
|||
ElMessage.error('请设置热区链接!');
|
||||
return;
|
||||
} else {
|
||||
hot_list.value.img_height = imgRef.value?.clientHeight || 0;
|
||||
hot_list.value.img_width = imgRef.value?.clientWidth || 0;
|
||||
modelValue.value = cloneDeep(hot_list.value);
|
||||
close_event();
|
||||
}
|
||||
|
|
@ -297,106 +423,5 @@ const confirm_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;
|
||||
padding: 0 0.4rem 0.4rem 0;
|
||||
}
|
||||
.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: #f0f0f0;
|
||||
border: 1px solid #333;
|
||||
right: -0.4rem;
|
||||
bottom: -0.4rem;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@import 'index.scss';
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,108 @@
|
|||
<template>
|
||||
<el-dialog v-model="dialog_visible_category_oprate" class="radius-lg" width="1168" append-to-body>
|
||||
<el-dialog v-model="dialog_visible_category_oprate" class="radius-lg" width="500" append-to-body>
|
||||
<template #header>
|
||||
<div class="title center re">
|
||||
<div class="tc size-16 fw">添加分类</div>
|
||||
<div class="tc size-16 fw">{{ type == 'add' ? '添加' : '编辑' }}附件分类</div>
|
||||
</div>
|
||||
</template>
|
||||
<div class="mt-16 pa-20">
|
||||
<el-form ref="ruleFormRef" :model="form" :rules="rules" label-width="60" status-icon>
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="路径" prop="path">
|
||||
<el-input v-model="form.path" placeholder="请输入路径"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序">
|
||||
<el-input v-model="form.sort" placeholder="请输入排序"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否启用">
|
||||
<el-switch v-model="form.is_enable"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button class="plr-28 ptb-10" @click="cancel_event(ruleFormRef)">取消</el-button>
|
||||
<el-button class="plr-28 ptb-10" type="primary" @click="confirm_event(ruleFormRef)">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
const dialog_visible_category_oprate = ref(false);
|
||||
import { FormInstance, FormRules } from 'element-plus';
|
||||
import { cloneDeep } from 'lodash';
|
||||
/**
|
||||
* @description: 分类操作
|
||||
* @param modelValue{uploadList[]} 默认值
|
||||
* @param visibleDialog{Boolean} 弹窗开启关闭
|
||||
* @param type{String} 新增add编辑edit
|
||||
* @return {*} update:modelValue confirm
|
||||
*/
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Object as PropType<Tree>,
|
||||
default: () => {},
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'add',
|
||||
},
|
||||
categoryId: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
const dialog_visible_category_oprate = defineModel({ type: Boolean, default: false });
|
||||
const form = ref<Tree>({
|
||||
id: '',
|
||||
name: '',
|
||||
path: '',
|
||||
sort: 0,
|
||||
is_enable: true,
|
||||
children: [],
|
||||
});
|
||||
watch(
|
||||
() => dialog_visible_category_oprate.value,
|
||||
(newValue) => {
|
||||
if (newValue && props.type !== 'add') {
|
||||
form.value = cloneDeep(props.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
const rules = reactive<FormRules>({
|
||||
name: [
|
||||
{ required: true, trigger: 'blur', message: '请输入名称' },
|
||||
{ min: 1, max: 60, message: '名称长度1~60个字符', trigger: 'blur' },
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: '请输入地址', trigger: 'blur' },
|
||||
{ min: 1, max: 230, message: '路径长度1~230个字符', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
onMounted(() => {
|
||||
if (props.type == 'add') {
|
||||
ruleFormRef.value?.resetFields();
|
||||
}
|
||||
});
|
||||
|
||||
const cancel_event = (formEl: FormInstance | undefined) => {
|
||||
dialog_visible_category_oprate.value = false;
|
||||
formEl?.resetFields();
|
||||
};
|
||||
const emit = defineEmits(['update:modelValue', 'confirm']);
|
||||
const confirm_event = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid, fields) => {
|
||||
if (valid) {
|
||||
emit('confirm', form.value, props.categoryId);
|
||||
cancel_event(formEl);
|
||||
} else {
|
||||
console.log('error submit!', fields);
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
// 分类树结构
|
||||
type Tree = {
|
||||
id: number | string;
|
||||
name: string;
|
||||
path: string;
|
||||
is_enable: boolean;
|
||||
sort: number;
|
||||
children: Tree[];
|
||||
};
|
||||
|
|
@ -19,10 +19,21 @@
|
|||
<icon name="search" size="18"></icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<icon name="add" size="18" @click="add_type"></icon>
|
||||
<icon name="add" size="18" class="c-pointer" @click="add_type"></icon>
|
||||
</div>
|
||||
<el-scrollbar height="490px">
|
||||
<el-tree ref="treeRef" class="filter-tree" :data="type_data" node-key="id" highlight-current :expand-on-click-node="false" :props="defaultProps" empty-text="无数据" default-expand-all :filter-node-method="filter_node" @node-click="tree_node_event" />
|
||||
<el-tree ref="treeRef" class="filter-tree" :data="type_data" node-key="id" highlight-current :expand-on-click-node="false" :props="defaultProps" empty-text="无数据" default-expand-all :filter-node-method="filter_node" @node-click="tree_node_event">
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node flex-row jc-sb align-c w pr-10">
|
||||
<span>{{ data.name }}</span>
|
||||
<span class="flex-row gap-10">
|
||||
<icon name="add" size="12" color="primary" @click="append_type_event(data)"></icon>
|
||||
<icon name="edit" size="12" color="primary" @click="edit_type_event(data)"></icon>
|
||||
<icon name="del" size="12" color="primary" @click="remove_type_event(node, data)"></icon>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<div class="right-content flex-1 flex-width">
|
||||
|
|
@ -159,8 +170,10 @@
|
|||
<!-- 图片预览 -->
|
||||
<el-image-viewer v-if="preview_switch_img && upload_type == 'img'" :z-index="999999" :url-list="[preview_url]" :hide-on-click-modal="true" @close="preview_close"></el-image-viewer>
|
||||
<upload-model v-model="upload_model_visible" :type="upload_type" :exts="props.type == 'img' ? ext_img_name_list : props.type == 'video' ? ext_video_name_list : ext_file_name_list" @close="close_upload_model"></upload-model>
|
||||
<form-upload-category v-model="upload_category_model_visible" :value="upload_category_model" :type="upload_category_type" :category-id="upload_category_id" @confirm="upload_category_confirm"></form-upload-category>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { get_math } from '@/utils/index';
|
||||
const app = getCurrentInstance();
|
||||
/**
|
||||
* @description: 图片上传
|
||||
|
|
@ -288,7 +301,7 @@ const upload_type_change = (type: any) => {
|
|||
const treeRef = ref();
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'label',
|
||||
label: 'name',
|
||||
};
|
||||
// 分类查询
|
||||
const search_filter = ref('');
|
||||
|
|
@ -303,31 +316,36 @@ const search_name = ref('');
|
|||
const page = ref(1);
|
||||
// 总数量
|
||||
const data_total = ref(0);
|
||||
interface Tree {
|
||||
id: number;
|
||||
label: string;
|
||||
children?: Tree[];
|
||||
}
|
||||
const filter_node = (value: string, data: any): boolean => {
|
||||
if (!value) return true;
|
||||
return data.label.indexOf(value) !== -1;
|
||||
return data.name.indexOf(value) !== -1;
|
||||
};
|
||||
const type_data: Tree[] = [
|
||||
const type_data = ref<Tree[]>([
|
||||
{
|
||||
id: 0,
|
||||
label: '全部图片',
|
||||
name: '全部图片',
|
||||
path: '全部',
|
||||
is_enable: true,
|
||||
sort: 1,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
label: '金刚区',
|
||||
children: [
|
||||
{
|
||||
id: 2,
|
||||
label: '金刚区 1-1',
|
||||
},
|
||||
],
|
||||
name: '全部视频',
|
||||
path: '全部',
|
||||
is_enable: true,
|
||||
sort: 2,
|
||||
children: [],
|
||||
},
|
||||
];
|
||||
{
|
||||
id: 2,
|
||||
name: '全部文件',
|
||||
path: '全部',
|
||||
is_enable: true,
|
||||
sort: 3,
|
||||
children: [],
|
||||
},
|
||||
]);
|
||||
// 图片/视频/文件移动至
|
||||
const category_list = [
|
||||
{
|
||||
|
|
@ -458,15 +476,82 @@ const search_data = ref({
|
|||
const get_list = () => {
|
||||
console.log('查询接口', search_data);
|
||||
};
|
||||
// 分类弹窗表单数据
|
||||
const upload_category_model = ref<Tree>({
|
||||
id: '',
|
||||
name: '',
|
||||
path: '',
|
||||
sort: 0,
|
||||
is_enable: false,
|
||||
children: [],
|
||||
});
|
||||
// 分类弹窗操作类型
|
||||
const upload_category_type = ref('add');
|
||||
// 分类弹窗开关
|
||||
const dialog_visible_type_oprate = ref(false);
|
||||
const upload_category_model_visible = ref(false);
|
||||
// 添加一级分类
|
||||
const add_type = () => {};
|
||||
const add_type = () => {
|
||||
upload_category_type.value = 'add';
|
||||
upload_category_model_visible.value = true;
|
||||
upload_category_id.value = '';
|
||||
};
|
||||
// 分类操作确认回调
|
||||
const upload_category_confirm = (data: any) => {
|
||||
if (upload_category_type.value == 'add') {
|
||||
// 添加分类
|
||||
// 判断是添加一级分类还是子级分类
|
||||
if (upload_category_id.value == '') {
|
||||
// 添加子级分类
|
||||
console.log('添加子级分类');
|
||||
// type_data.value = type_data.value.map((item: any) => {
|
||||
// if (item.id == upload_category_id.value) {
|
||||
// item.children.push(data);
|
||||
// }
|
||||
// return item;
|
||||
// });
|
||||
} else {
|
||||
// 添加一级分类
|
||||
console.log('添加一级分类');
|
||||
// type_data.value = [...type_data.value, data];
|
||||
}
|
||||
} else if (upload_category_type.value == 'edit') {
|
||||
// 编辑分类
|
||||
console.log('编辑分类');
|
||||
// type_data.value = type_data.value.map((item: any) => {
|
||||
// if (item.id == data.id) {
|
||||
// item = data;
|
||||
// }
|
||||
// return item;
|
||||
// });
|
||||
}
|
||||
};
|
||||
// 左侧分类树结构节点点击事件
|
||||
const tree_node_event = (data: any) => {
|
||||
// search_filter.value = data.id;
|
||||
get_list();
|
||||
};
|
||||
const upload_category_id = ref<number | string>('');
|
||||
// 添加子分类
|
||||
const append_type_event = (data: Tree) => {
|
||||
upload_category_type.value = 'add';
|
||||
upload_category_id.value = data.id;
|
||||
upload_category_model_visible.value = true;
|
||||
};
|
||||
// 编辑子分类
|
||||
const edit_type_event = (data: Tree) => {
|
||||
upload_category_type.value = 'edit';
|
||||
upload_category_id.value = data.id;
|
||||
upload_category_model_visible.value = true;
|
||||
upload_category_model.value = data;
|
||||
};
|
||||
// 删除分类(Node报错,node使用any)
|
||||
const remove_type_event = (node: any, data: Tree) => {
|
||||
const parent = node.parent;
|
||||
const children: Tree[] = parent.data.children || parent.data;
|
||||
const index = children.findIndex((d) => d.id === data.id);
|
||||
children.splice(index, 1);
|
||||
// type_data.value = [...type_data.value];
|
||||
};
|
||||
// 确认
|
||||
const confirm_event = () => {
|
||||
dialog_visible.value = false;
|
||||
|
|
|
|||
|
|
@ -53,13 +53,7 @@ watch(
|
|||
}
|
||||
);
|
||||
const modelValue = defineModel({ type: Object, default: {} });
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
link: string;
|
||||
}
|
||||
const tableData: User[] = [
|
||||
const tableData: linkData[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '一级分类一级分类一级分类一级分类一级分类一级分类一级分类',
|
||||
|
|
@ -99,7 +93,7 @@ const template_selection = ref('');
|
|||
|
||||
const row_click = (row: any) => {
|
||||
const new_table_data = JSON.parse(JSON.stringify(tableData));
|
||||
template_selection.value = new_table_data.findIndex((item: User) => item.id == row.id).toString();
|
||||
template_selection.value = new_table_data.findIndex((item: linkData) => item.id == row.id).toString();
|
||||
modelValue.value = row;
|
||||
};
|
||||
//#region 分页 -----------------------------------------------start
|
||||
|
|
|
|||
|
|
@ -129,18 +129,11 @@ const address = computed(() => {
|
|||
});
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
const emit = defineEmits(['update:link', 'required']);
|
||||
interface formType {
|
||||
id?: number;
|
||||
name: string;
|
||||
link: string;
|
||||
lng?: number;
|
||||
lat?: number;
|
||||
}
|
||||
const on_submit = () => {
|
||||
if (!ruleFormRef.value) return;
|
||||
ruleFormRef.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
let new_value: formType = {
|
||||
let new_value: linkData = {
|
||||
name: '',
|
||||
link: '',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,15 +50,7 @@ watch(
|
|||
}
|
||||
);
|
||||
const modelValue = defineModel({ type: Object, default: {} });
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
link: string;
|
||||
hasChildren?: boolean;
|
||||
children?: User[];
|
||||
}
|
||||
const tableData: User[] = [
|
||||
const tableData: linkData[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '一级分类',
|
||||
|
|
@ -106,9 +98,9 @@ const options = ref([
|
|||
const template_selection = ref('');
|
||||
|
||||
const row_click = (row: any) => {
|
||||
let new_data: User[] = [];
|
||||
let new_data: linkData[] = [];
|
||||
// 多级数组转换一级数组
|
||||
const array_conversion = (item: User[]) => {
|
||||
const array_conversion = (item: linkData[]) => {
|
||||
item.forEach((item) => {
|
||||
new_data.push(item);
|
||||
if (item.children) {
|
||||
|
|
@ -118,7 +110,7 @@ const row_click = (row: any) => {
|
|||
return new_data;
|
||||
};
|
||||
const new_table_data = array_conversion(JSON.parse(JSON.stringify(tableData)));
|
||||
template_selection.value = new_table_data.findIndex((item: User) => item.id == row.id).toString();
|
||||
template_selection.value = new_table_data.findIndex((item: linkData) => item.id == row.id).toString();
|
||||
modelValue.value = row;
|
||||
};
|
||||
//#region 分页 -----------------------------------------------start
|
||||
|
|
|
|||
|
|
@ -197,16 +197,11 @@ const rules = ref<FormRules>({
|
|||
key: [{ required: true, trigger: 'change', message: '关键词不能为空' }],
|
||||
});
|
||||
const ruleFormRef = ref<FormInstance>();
|
||||
interface formType {
|
||||
id?: number;
|
||||
name: string;
|
||||
link: string;
|
||||
}
|
||||
const on_submit = () => {
|
||||
if (!ruleFormRef.value) return;
|
||||
ruleFormRef.value.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
let new_value: formType = {
|
||||
let new_value: linkData = {
|
||||
name: form.key,
|
||||
link: form.key,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -50,13 +50,7 @@ watch(
|
|||
}
|
||||
);
|
||||
const modelValue = defineModel({ type: Object, default: {} });
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
icon: string;
|
||||
link: string;
|
||||
}
|
||||
const tableData: User[] = [
|
||||
const tableData: linkData[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '一级分类',
|
||||
|
|
@ -98,7 +92,7 @@ const template_selection = ref('');
|
|||
|
||||
const row_click = (row: any) => {
|
||||
const new_table_data = JSON.parse(JSON.stringify(tableData));
|
||||
template_selection.value = new_table_data.findIndex((item: User) => item.id == row.id).toString();
|
||||
template_selection.value = new_table_data.findIndex((item: linkData) => item.id == row.id).toString();
|
||||
modelValue.value = row;
|
||||
};
|
||||
//#region 分页 -----------------------------------------------start
|
||||
|
|
|
|||
|
|
@ -40,13 +40,7 @@ watch(
|
|||
);
|
||||
const modelValue = defineModel({ type: Object, default: {} });
|
||||
const search_value = ref('');
|
||||
interface Data {
|
||||
id: number;
|
||||
name: string;
|
||||
link?: String;
|
||||
data?: Data[];
|
||||
}
|
||||
const base_data = ref<Data[]>([
|
||||
const base_data = ref<linkData[]>([
|
||||
{
|
||||
id: 0,
|
||||
name: '基础链接',
|
||||
|
|
@ -101,7 +95,7 @@ const handle_search = () => {
|
|||
};
|
||||
const menu_active = ref('');
|
||||
const emit = defineEmits(['update:link']);
|
||||
const menu_link_event = (item: Data, parent_id: number) => {
|
||||
const menu_link_event = (item: linkData, parent_id: number | undefined) => {
|
||||
if (`${parent_id}-${item.id}` == menu_active.value) {
|
||||
menu_active.value = '';
|
||||
modelValue.value = {};
|
||||
|
|
|
|||
|
|
@ -40,12 +40,7 @@ watch(
|
|||
}
|
||||
);
|
||||
const modelValue = defineModel({ type: Object, default: {} });
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
link: string;
|
||||
}
|
||||
const tableData: User[] = [
|
||||
const tableData: linkData[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: '一级分类',
|
||||
|
|
@ -76,7 +71,7 @@ const template_selection = ref('');
|
|||
|
||||
const row_click = (row: any) => {
|
||||
const new_table_data = JSON.parse(JSON.stringify(tableData));
|
||||
template_selection.value = new_table_data.findIndex((item: User) => item.id == row.id).toString();
|
||||
template_selection.value = new_table_data.findIndex((item: linkData) => item.id == row.id).toString();
|
||||
modelValue.value = row;
|
||||
};
|
||||
//#region 分页 -----------------------------------------------start
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ declare module 'vue' {
|
|||
ColorPicker: typeof import('./../components/base/color-picker/index.vue')['default']
|
||||
CommonStyles: typeof import('./../components/common/common-styles/index.vue')['default']
|
||||
Components: typeof import('./../components/model-custom/components/index.vue')['default']
|
||||
copy: typeof import('./../components/common/hot/index copy.vue')['default']
|
||||
Dialog: typeof import('./../components/model-custom/components/dialog.vue')['default']
|
||||
Drag: typeof import('./../components/base/drag/index.vue')['default']
|
||||
ElBadge: typeof import('element-plus/es')['ElBadge']
|
||||
|
|
|
|||
|
|
@ -97,6 +97,21 @@ declare global {
|
|||
background_img_url: uploadList[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 链接参数
|
||||
*/
|
||||
type linkData = {
|
||||
id?: number;
|
||||
name: string;
|
||||
link?: String;
|
||||
data?: Data[];
|
||||
icon?: string;
|
||||
lng?: number;
|
||||
lat?: number;
|
||||
hasChildren?: boolean;
|
||||
children?: linkData[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 热区参数
|
||||
*/
|
||||
|
|
@ -116,7 +131,7 @@ declare global {
|
|||
drag_start: rectCoords;
|
||||
drag_end: rectCoords;
|
||||
name: string;
|
||||
link: object;
|
||||
link: linkData;
|
||||
};
|
||||
}
|
||||
export {};
|
||||
|
|
|
|||
Loading…
Reference in New Issue