312 lines
8.8 KiB
Vue
312 lines
8.8 KiB
Vue
<template>
|
|
<div>
|
|
<div class="decorate-cube">
|
|
<ul v-for="(n, index) in densityNum" :key="index" class="cube-col">
|
|
<li v-for="(i, index1) in densityNum" :key="index1" class="cube-item" :style="{ width: cubeCellWidth + 'px', height: cubeCellHeight + 'px' }" :data-x="n" :data-y="i" @click="onClickCubeItem($event)" @mouseenter="onEnterCubeItem($event)">
|
|
<div :class="['w h item', { 'item-selecting': isSelecting(n, i), 'item-selected': isSelected(n, i) }]">
|
|
<icon name="add" color="9" :style="{ 'line-height': cubeCellHeight + 'px' }"></icon>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
<div v-for="(item, index) in selectedList" :key="index" :class="['cube-selected', {'cube-selected_active': selected_active == index }]" :style="selected_style(item)" @click="selected_click(index)">
|
|
<div v-if="selected_active == index && props.flag" class="cube-del" @click.stop="on_selected_del(index)">
|
|
<icon name="close" color="f" size="8"></icon>
|
|
</div>
|
|
<template v-if="!isEmpty(item.img[0]) && props.type == 'img'">
|
|
<image-empty v-model="item.img[0]"></image-empty>
|
|
</template>
|
|
<template v-else>
|
|
<div class="cube-selected-text">
|
|
{{ Math.round((750 / densityNum) * (item.end.y - item.start.y + 1)) }}
|
|
x
|
|
{{ Math.round((750 / densityNum) * (item.end.x - item.start.x + 1)) }}
|
|
像素
|
|
<template v-if="props.type == 'data'">
|
|
<div class="mt-12">共有3个商品</div>
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { isEmpty } from 'lodash';
|
|
interface CubeItem {
|
|
start: {
|
|
x: number;
|
|
y: number;
|
|
};
|
|
end: {
|
|
x: number;
|
|
y: number;
|
|
};
|
|
img: uploadList[],
|
|
img_list?: any,
|
|
data_list?: any
|
|
}
|
|
|
|
interface Props {
|
|
flag: boolean;
|
|
list: CubeItem[];
|
|
type: string;
|
|
cubeWidth: number;
|
|
cubeHeight: number;
|
|
}
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
list: () => [],
|
|
flag: false,
|
|
type: 'img',
|
|
cubeWidth: 390,
|
|
cubeHeight: 390,
|
|
});
|
|
|
|
const selected_active = ref(0);
|
|
//#region 容器大小变更
|
|
const density = ref('4');
|
|
//#endregion
|
|
const selectingItem = reactive<any>({
|
|
tempStart: null,
|
|
tempEnd: null,
|
|
start: null,
|
|
end: null,
|
|
});
|
|
|
|
const selectedList = ref(props.list);
|
|
|
|
//单元魔方宽度。
|
|
const cubeCellWidth = computed(() => props.cubeWidth / parseInt(density.value));
|
|
//密度值。
|
|
const densityNum = computed(() => parseInt(density.value));
|
|
//单元魔方高度。
|
|
const cubeCellHeight = computed(() => props.cubeHeight / parseInt(density.value));
|
|
const emits = defineEmits(['selected_click']);
|
|
|
|
// 判断选择的内容的长度是否发生变化
|
|
watch(() => selectedList.value.length, (val) => {
|
|
if (val > 1) {
|
|
selected_active.value = val - 1;
|
|
} else {
|
|
selected_active.value = 0;
|
|
}
|
|
emits('selected_click', selected_active.value);
|
|
}, {deep: true});
|
|
|
|
const updateSelecting = () => {
|
|
//获取开始和结束之间的所有魔方单元。
|
|
const tempStart = selectingItem.tempStart;
|
|
const tempEnd = selectingItem.tempEnd;
|
|
|
|
selectingItem.start = {
|
|
x: Math.min(tempStart.x, tempEnd.x),
|
|
y: Math.min(tempStart.y, tempEnd.y),
|
|
};
|
|
selectingItem.end = {
|
|
x: Math.max(tempStart.x, tempEnd.x),
|
|
y: Math.max(tempStart.y, tempEnd.y),
|
|
};
|
|
};
|
|
//清除正在选择的。
|
|
const clearSelecting = () => {
|
|
selectingItem.tempStart = null;
|
|
selectingItem.tempEnd = null;
|
|
selectingItem.start = null;
|
|
selectingItem.end = null;
|
|
};
|
|
|
|
const coordFromCubeEvent = (event: any) => {
|
|
var el = event.currentTarget;
|
|
var x = el.getAttribute('data-x');
|
|
var y = el.getAttribute('data-y');
|
|
return { x: x, y: y };
|
|
};
|
|
|
|
const isContain = (x: number, y: number, item: CubeItem) => {
|
|
return item.start.x <= x && x <= item.end.x && item.start.y <= y && y <= item.end.y;
|
|
};
|
|
//魔方点击事件。
|
|
const onClickCubeItem = (event: any) => {
|
|
let domclass = event.currentTarget.getAttribute('class');
|
|
if (-1 !== domclass.indexOf('item-selected')) {
|
|
return;
|
|
}
|
|
|
|
let coord = coordFromCubeEvent(event);
|
|
|
|
if (null == selectingItem.tempStart) {
|
|
selectingItem.tempStart = coord;
|
|
selectingItem.tempEnd = coord;
|
|
selectingItem.start = coord;
|
|
selectingItem.end = coord;
|
|
return;
|
|
}
|
|
|
|
selectingItem.tempEnd = coord;
|
|
updateSelecting();
|
|
|
|
//加入选中的。
|
|
let selectedItem = {
|
|
start: selectingItem.start,
|
|
end: selectingItem.end,
|
|
img: [],
|
|
};
|
|
selectedList.value.push(selectedItem);
|
|
clearSelecting();
|
|
};
|
|
|
|
const onEnterCubeItem = (event: any) => {
|
|
if (selectingItem.tempStart) {
|
|
var coord = coordFromCubeEvent(event);
|
|
selectingItem.tempEnd = coord;
|
|
updateSelecting();
|
|
}
|
|
};
|
|
// 删除当前选中的内容
|
|
const on_selected_del = (index: number) => {
|
|
clearSelecting();
|
|
// splice() 会先从原数组中添加/删除项目 然后返回被删除的项目。
|
|
selectedList.value.splice(index, 1);
|
|
};
|
|
//判断是否正在选择
|
|
const isSelecting = (x: number, y: number) => {
|
|
const item = selectingItem;
|
|
if (item.tempStart) {
|
|
return isContain(x, y, item);
|
|
}
|
|
return false;
|
|
};
|
|
//判断是否已经选择。
|
|
const isSelected = (x: number, y: number) => {
|
|
for (var i = 0; i < selectedList.value.length; i++) {
|
|
if (isContain(x, y, selectedList.value[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
//计算选中层的宽度。
|
|
const getSelectedWidth = (item: CubeItem) => {
|
|
return (item.end.x - item.start.x + 1) * cubeCellWidth.value;
|
|
};
|
|
//计算选中层的高度。
|
|
const getSelectedHeight = (item: CubeItem) => {
|
|
return (item.end.y - item.start.y + 1) * cubeCellHeight.value;
|
|
};
|
|
//计算选中层的右边距离。
|
|
const getSelectedTop = (item: CubeItem) => {
|
|
return (item.start.y - 1) * cubeCellHeight.value;
|
|
};
|
|
//计算选中层的左边距离。
|
|
const getSelectedLeft = (item: CubeItem) => {
|
|
return (item.start.x - 1) * cubeCellWidth.value;
|
|
};
|
|
// 生成的样式
|
|
const selected_style = (item: CubeItem) => {
|
|
return `width: ${ getSelectedWidth(item) }px; height: ${ getSelectedHeight(item) }px; top: ${ getSelectedTop(item) }px; left: ${ getSelectedLeft(item) }px;`
|
|
}
|
|
// 选中的点击事件
|
|
const selected_click = (index: number) => {
|
|
selected_active.value = index;
|
|
emits('selected_click', index);
|
|
};
|
|
const tips = () => {
|
|
console.log(selectedList.value[selected_active.value].img_list);
|
|
console.log(selectedList.value[selected_active.value].data_list);
|
|
};
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.decorate-cube {
|
|
position: relative;
|
|
.cube-col {
|
|
float: left;
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
.cube-item {
|
|
background: #f5f5f5;
|
|
cursor: pointer;
|
|
text-align: center;
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
.item {
|
|
border: 1px solid #fff;
|
|
border-top: 0;
|
|
}
|
|
.item-selecting {
|
|
background: #e0edff;
|
|
position: absolute;
|
|
z-index: 99999;
|
|
}
|
|
.item-selected {
|
|
background: #e0edff;
|
|
}
|
|
}
|
|
.cube-item:first-child {
|
|
.item {
|
|
border-top: 0;
|
|
}
|
|
}
|
|
.cube-item:last-child {
|
|
.item {
|
|
border-bottom: 0;
|
|
}
|
|
}
|
|
.cube-col:first-child {
|
|
.cube-item .item {
|
|
border-left: 0;
|
|
}
|
|
}
|
|
.cube-col:last-of-type {
|
|
.cube-item .item {
|
|
border-right: 0;
|
|
}
|
|
}
|
|
.cube-selected-text {
|
|
font-size: 12px;
|
|
width: 100%;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translateX(-50%) translateY(-50%);
|
|
}
|
|
.cube-selected {
|
|
position: absolute;
|
|
background-color: #eee;
|
|
border: 1px solid #fff;
|
|
text-align: center;
|
|
color: $cr-primary;
|
|
cursor: pointer;
|
|
box-sizing: border-box;
|
|
z-index: 2;
|
|
}
|
|
.cube-selected_active {
|
|
border: 1px solid $cr-primary;
|
|
}
|
|
.cube-del {
|
|
background: $cr-primary;
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
height: 1.6rem;
|
|
width: 1.6rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1;
|
|
}
|
|
:deep(.el-image) {
|
|
height: 100%;
|
|
width: 100%;
|
|
background-color: #fff;
|
|
.el-image__inner {
|
|
object-fit: cover;
|
|
}
|
|
.image-slot img {
|
|
width: 6rem;
|
|
}
|
|
}
|
|
}
|
|
</style>
|