开箱即用的火热的店铺装修魔方组件(Vue)

966 阅读11分钟

原文链接 juejin.cn/post/734695…

博主是用 react 写的,小编借鉴博主的思路实现了vue的,源码在最后,没有进行太多的封装,至于为什么呢,大家都懂 代码实现细节请关注源码注释

背景:多元化的炫酷商城首页,C段用户各不相同的需求,魔方组件的多样化布局和自定义链接应运而生。

效果

image-20240901160546171.png

功能特点

  • 多样化的布局选择

    • 固定宽满屏<750rpx>,高等比例<750rpx>

    • 布局选择

      • 预定义10种布局

      image-20240901161142373.png image-20240901161154212.png

      • 风格11 自定义模板支持鼠标选定布局大小,范围切换动态调整选中块大小,支持 4 => 7(行*列) image-20240901162312511.png
  • 支持上传商品图片或者其他宣传海报,并配置相关链接

image-20240901161747445.png

  • 可调整图片间隙和尺寸要求,适配不同的布局需求

image-20240901162008861.png

思路预演<直角坐标系辅佐,左上角为原点,以横轴最大等分块为1单位>

  • 风格1上下各一个,风格2左右各一个,以坐标系为例,将坐标系等分为4个全等的依次排列的正方形,风格一就可等价为宽为2,高为1,纵轴排列的两个长方形,同理,风格2就是横轴排列的两个宽为1,高为2的长方形
  • 以此,风格八就是等分的四个块以此排列
  • 风格三就是等分的9块,横轴排列的宽为1,高为3的长方形
  • 风格4-7,就是等分的四块进行各方向排列
  • 这样一来,等分块进行分割坐标系之后,是不是就是很简单啦

image-20240911230326707.png

数据格式

// 风格一到风格十通用;风格十一也一样,主要是风格十一有一个用户选择和防重复校验的问题。
magic_1: {
    // 以横轴最大等分块
    diyCubeNumber: 2,
    // 块数据
    diyCubeResult: [{ // 最小单元格数据,风格通用
        x: 0, // 横轴坐标点
        y: 0, // 纵轴坐标点
        height: 1, // 上文思路所讲述的高度
        width: 2, // 上文思路所讲述的宽度
        url: '', // 该设定单元格所绑定的适用图片链接
        link: '', // 该设定单元格所绑定的适用跳转链接(移动端是 uniapp 开发的小程序)
        linkname: '' // 该设定单元格所绑定的适用跳转链接名称
    }, {
        x: 0,
        y: 1,
        height: 1,
        width: 2,
        url: '',
        link: '',
        linkname: ''
    }]
}

风格十一实现思路

总结:两层dom重叠,底层正常排列进行选取,上层利用单元格单位长度进行定位

主要代码

<div class="right-setting-box-item magic-box" @mouseleave="handleMouseLeave"
    :style="{width: itemWidth * item.diyCubeNumber +'px', height: itemWidth * item.diyCubeNumber +'px'}">
    <template v-if="item.comp_style == 11">
        <!-- 默认画板数据 -->
        <div class="defaule-row" v-for="(item,index) in diyCubeData" :key="index">
            <div class="defaule-col" :class="{'item-seleting': currentCubeKey.includes(mergeKey(index, index2))}"
                :style="{width: itemWidth * item2.width +'px',height: itemWidth * item2.height+'px'}"
                v-for="(item2 ,index2) in item" :key="index2" @click="handleDefaultMagic(item2)"
                @mouseover.stop="mouseoverDefaultMagic(item2)">
                <i class="iconfont ttdiy-add"></i>
            </div>
        </div>
    </template>
​
    <!-- 选中定位的画板 -->
    <template v-if="item.diyCubeResult.length">
        <div class="diy-magic-item" v-for="(it,idx) in item.diyCubeResult" :key="idx+100" :style="getStyle(it)"
            :class="{'active': idx == currentCubeIndex}" @click="clickDiyMagic(idx)">
            <i class="el-icon-error" @click.stop="removeDiyMagic(idx)"></i>
            <img v-if="it.url" :src="it.url">
            <span v-else>{{ `${Math.floor(it.width * itemWidth)} * ${Math.floor(it.height * itemWidth)}` }}</span>
        </div>
    </template>
</div><script>
export default{
    data(){
        return{
            // 容器的最大宽度
            maxBoxWidth: 375,
        }
    },
    computed: {
        // 计算每个单元格的宽度
        itemWidth() {
            return this.maxBoxWidth / this.item.diyCubeNumber
        }
    },
    methods: {
        getStyle(item) {
            const { x, y, width, height } = item;
            let style = {
                left: `${x * this.itemWidth}px`,
                top: `${y * this.itemWidth}px`,
                width: `${width * this.itemWidth}px`,
                height: `${height * this.itemWidth}px`
            }
            return style
        }
    }
}
</script>

重点关注 底层的默认画板数据和已选中的数据

clickDiyMagic 这个方法,主要是操作已生成的块进行绑定数据

handleDefaultMagic 这个方法,用户开始操作选中

mouseoverDefaultMagic 这个方法,主要处理用户操作鼠标移动的时候,其余块是否可选,核心判定也在这个方法里面,卖个关子

整个魔方的难重点

矩形重叠的判定(图片借用)

20240324_140754_isRectangleOverlap.gif

在鼠标移动过程中,如果当前选择块和已经存在的块重叠,就不做处理,所以就要把选择块和已存在块进行校验处理

// 魔方默认块的移入事件
mouseoverDefaultMagic(item2) {
    // console.log(item2);
    // 如果当前不是编辑状态,则直接返回
    if (!this.cubeEdit) return
​
    // 定义一个空数组,用于存储当前魔方块的key
    const keys = [];
    // 取出当前魔方块的首位数据
    const start = this.untieKey(this.currentCubeStartKey)
    // 取出当前魔方块的结尾数据
    const end = { ...item2 }
    // 处理魔方块的y和x,从小到大,方便后续计算、追加数据
    const ys = [start.y, end.y].sort()
    const xs = [start.x, end.x].sort()
​
    // 判定是否重叠
    if (this.antiCollision(start, end)) {
        return;
    }
​
    for (let i = ys[0]; i <= ys[1]; i++) {
        for (let j = xs[0]; j <= xs[1]; j++) {
            keys.push(this.mergeKey(i, j))
        }
    }
​
    this.currentCubeKey = keys
},
antiCollision(start, end) {
    // 有可能从右边从左边选,导致 start.x > end.y,所以要排序
    // 保证 rec1 [x1,y1,x2,y2] (x1,y1)为左上角坐标,(x2,y2)为右下角坐标
    const xs = [start.x, end.x].sort();
    const ys = [start.y, end.y].sort();
    // 因为 end 记录的是 li 块的左上角坐标,构成矩形区域的话,应该算 li 块右下角坐标,所以 + 1
    const rec1 = [xs[0], ys[0], xs[1] + 1, ys[1] + 1]
    // console.log(rec1, 'rec1');
    for (let i = 0; i < this.item.diyCubeResult.length; i++) {
        const item = this.item.diyCubeResult[i];
        const rec2 = [item.x, item.y, item.x + item.width, item.y + item.height]
        // console.log(rec2, 'rec2');
        const isIntersect = this.isIntersect(rec1, rec2);
        if (isIntersect) return true
    }
    return false
},
// 核心判定两个矩形是否重叠
isIntersect(rec1, rec2) {
    /**
     * 核心
     * 思想:两矩形右边界的最小值大于两矩形左边界的最大值,说明横轴两重叠,两矩形下边界的最小值大于两矩形上边界的最大值,说明纵轴两重叠,两者必须为真
    */
    return (Math.min(rec1[2], rec2[2]) > Math.max(rec1[0], rec2[0]) &&
        Math.min(rec1[3], rec2[3]) > Math.max(rec1[1], rec2[1]));
},

以上对于魔方的思路讲解就到这里,是不是很简单

源码

源码是跟项目走的,自行摘取 自定义魔方相关逻辑

项目是 vue2 + element UI 实现的

// 右侧的配置板块
<template>
    <div class="right-title-box magic-cube">
        <div class="right-componet-name">
            <img :src="require(`@/assets/decoration/images/${idToIconMap[item.comp_id].icon}`)" />
            <span>{{ idToIconMap[item.comp_id].label }}</span>
        </div>
        <div class="right-cat-line"></div>
        <div class="right-setting-box">
            <div class="right-setting-box-title">选择风格</div>
            <div class="right-setting-box-item">
                <el-button type="primary" size="small" @click="openStyleDialog">选择风格</el-button>
                <span style="margin-left: 16px">当前:风格{{ item.comp_style }}</span>
            </div>
        </div>
        <div class="right-cat-line"></div>
        <div class="right-setting-box">
            <div class="right-setting-box-title">魔方布局</div>
            <div class="right-setting-box-item" v-if="item.comp_style == 11">
                <div class="label">魔方密度</div>
                <el-select v-model="item.diyCubeNumber" @change="handleNumberChange">
                    <el-option v-for="it in diyCubeNumberEnum" :key="it.value" :label="it.label" :value="it.value"></el-option>
                </el-select>
            </div>
            <div class="right-setting-box-item magic-box" @mouseleave="handleMouseLeave"
                :style="{width: itemWidth * item.diyCubeNumber +'px', height: itemWidth * item.diyCubeNumber +'px'}">
                <template v-if="item.comp_style == 11">
                    <!-- 默认画板数据 -->
                    <div class="defaule-row" v-for="(item,index) in diyCubeData" :key="index">
                        <div class="defaule-col" :class="{'item-seleting': currentCubeKey.includes(mergeKey(index, index2))}"
                            :style="{width: itemWidth * item2.width +'px',height: itemWidth * item2.height+'px'}"
                            v-for="(item2 ,index2) in item" :key="index2" @click="handleDefaultMagic(item2)"
                            @mouseover.stop="mouseoverDefaultMagic(item2)">
                            <i class="iconfont ttdiy-add"></i>
                        </div>
                    </div>
                </template><!-- 选中定位的画板 -->
                <template v-if="item.diyCubeResult.length">
                    <div class="diy-magic-item" v-for="(it,idx) in item.diyCubeResult" :key="idx+100" :style="getStyle(it)"
                        :class="{'active': idx == currentCubeIndex}" @click="clickDiyMagic(idx)">
                        <i class="el-icon-error" @click.stop="removeDiyMagic(idx)"></i>
                        <img v-if="it.url" :src="it.url">
                        <span v-else>{{ `${Math.floor(it.width * itemWidth)} * ${Math.floor(it.height * itemWidth)}` }}</span>
                    </div>
                </template>
            </div>
            <template v-if="item.diyCubeResult.length">
                <div class="right-setting-box-title">数据</div>
                <div class="right-setting-box-item" style="padding-left: 10px;">
                    <ImgPicker :image_obj="item.diyCubeResult[currentCubeIndex]" image_key="url"></ImgPicker>
                </div>
                <div class="right-setting-box-item" style="padding-left: 10px;">
                    <LinkPicker :linkobj="item.diyCubeResult[currentCubeIndex]" link_key="link" linkname_key="linkname">
                    </LinkPicker>
                </div>
            </template>
        </div>
        <div class="right-cat-line"></div>
        <div class="right-setting-box">
            <div class="right-setting-box-title">背景设置</div>
            <div class="right-setting-box-item">
                <div class="label">底部背景</div>
                <ColorPicker :color_obj="item" color_key="comp_bg"></ColorPicker>
            </div>
        </div>
        <div class="right-cat-line"></div>
        <div class="right-setting-box">
            <div class="right-setting-box-title">边距设置</div>
            <div class="right-setting-box-item">
                <div class="label">图片间距</div>
                <SliderPicker :slider_obj="item" slider_key="image_space" :min="0" :max="20" append="px"></SliderPicker>
            </div>
            <div class="right-setting-box-item">
                <div class="label">上边距</div>
                <SliderPicker :slider_obj="item" slider_key="padding_top" :min="0" :max="50" append="px"></SliderPicker>
            </div>
            <div class="right-setting-box-item">
                <div class="label">下边距</div>
                <SliderPicker :slider_obj="item" slider_key="padding_bottom" :min="0" :max="50" append="px"></SliderPicker>
            </div>
            <div class="right-setting-box-item">
                <div class="label">左右边距</div>
                <SliderPicker :slider_obj="item" slider_key="padding_left_right" :min="0" :max="20" append="px"></SliderPicker>
            </div>
        </div>
        <div class="right-cat-line"></div>
        <div class="right-setting-box">
            <div class="right-setting-box-title">圆角设置</div>
            <div class="right-setting-box-item">
                <div class="label">上圆角</div>
                <SliderPicker :slider_obj="item" slider_key="top_radius" :min="0" :max="20" append="px"></SliderPicker>
            </div>
            <div class="right-setting-box-item">
                <div class="label">下圆角</div>
                <SliderPicker :slider_obj="item" slider_key="bottom_radius" :min="0" :max="20" append="px"></SliderPicker>
            </div>
        </div><!-- 风格选择 -->
        <el-dialog title="风格选择器" :visible.sync="styleDialogVisible" width="50%">
            <div class="dialog-box">
                <div class="img-item-box" v-for="it in 11" :key="it">
                    <div class="img-item" :class="{ active: styleDialogValue == it }" @click="styleDialogValue = it">
                        <img :src="require(`@/assets/decoration/images/cube/cube${it}.svg`)" />
                    </div>
                    <span>风格{{ it }}</span>
                </div>
            </div>
            <div slot="footer" class="dialog-footer">
                <el-button @click="styleDialogVisible = false">取 消</el-button>
                <el-button type="primary" @click="closeStyleDialog">确 定</el-button>
            </div>
        </el-dialog>
    </div>
</template><script>
    // 默认布局,用于风格切换
    import magic_default from '../../js/magic_default'
    import { idToIconMap } from "../../js/left_menu";
​
​
    const ImgPicker = () => import("../ImgPicker.vue");
    const SliderPicker = () => import("../SliderPicker.vue");
    const ColorPicker = () => import("../ColorPicker.vue");
    const LinkPicker = () => import("../LinkPicker.vue");
​
    export default {
        data() {
            return {
                idToIconMap,
                // 定义最小单元格数据模板
                cube: {
                    "x": 0, // 小块的横向位置
                    "y": 0, // 小块的纵向位置
                    "height": 1, // 小块的宽度占比
                    "width": 1, // 小块的高度占比
                },
                diyCubeNumberEnum: [{
                    value: 4,
                    label: '4 * 4'
                }, {
                    value: 5,
                    label: '5 * 5'
                }, {
                    value: 6,
                    label: '6 * 6'
                }, {
                    value: 7,
                    label: '7 * 7'
                }],
                // 魔方数据
                diyCubeData: [],
                // 容器的最大宽度
                maxBoxWidth: 375,
                // 当前单元块是否在编辑
                cubeEdit: false,
                // 当前编辑的key数组
                currentCubeKey: [],
                // 开始编辑的key
                currentCubeStartKey: '',
                // 操作生成的魔方数据选中下标
                currentCubeIndex: 0,
                // 魔方样式弹窗
                styleDialogVisible: false,
                styleDialogValue: 1
            }
        },
        props: ['item'],
        components: {
            ImgPicker,
            SliderPicker,
            ColorPicker,
            LinkPicker
        },
        created() {
            this.init()
        },
        computed: {
            // 计算每个单元格的宽度
            itemWidth() {
                return this.maxBoxWidth / this.item.diyCubeNumber
            }
        },
        methods: {
            openStyleDialog() {
                this.styleDialogValue = this.item.comp_style;
                this.styleDialogVisible = true;
            },
            closeStyleDialog() {
                this.item.comp_style = this.styleDialogValue;
                // 切换风格注意切换默认值
                this.currentCubeIndex = 0;
                if (this.styleDialogValue == 11) {
                    this.item.diyCubeNumber = 4;
                    this.item.diyCubeResult = [];
                    this.styleDialogVisible = false;
                    this.initDefault()
                } else {
                    const _traght = 'magic_' + this.styleDialogValue;
                    const _value = JSON.parse(JSON.stringify(magic_default[_traght]));
                    this.item.diyCubeNumber = _value.diyCubeNumber;
                    this.item.diyCubeResult = _value.diyCubeResult;
                    this.styleDialogVisible = false;
                }
            },
            handleNumberChange(e) {
                this.item.diyCubeNumber = e;
                this.initDefault()
            },
​
​
            // **************************************************************************** 自定义魔方相关
            init() {
                this.initDefault()
                if (!this.item.diyCubeResult.length) {
                    const _traght = 'magic_1';
                    const _value = JSON.parse(JSON.stringify(magic_default[_traght]));
                    this.item.diyCubeNumber = _value.diyCubeNumber;
                    this.item.diyCubeResult = _value.diyCubeResult;
                }
            },
            // 处理默认魔方数据
            initDefault() {
                // 此处布局为先行后列,所以i是纵轴y坐标,j是横轴x坐标
                this.diyCubeData = [];
                for (let i = 0; i < this.item.diyCubeNumber; i++) {
                    let _arr = []
                    for (let j = 0; j < this.item.diyCubeNumber; j++) {
                        let cube = { ...this.cube }
                        cube.x = j
                        cube.y = i
                        _arr.push(cube)
                    }
                    this.diyCubeData.push(_arr)
                }
            },
            // 处理默认魔方块点击事件
            handleDefaultMagic(item) {
                if (!this.cubeEdit) {
                    // 将当前点击位置打标点
                    const key = this.mergeKey(item.y, item.x);
                    // 为了防止鼠标不移动,将当前块设为单独块
                    this.currentCubeKey.push(key);
                    // 记录开始位置
                    this.currentCubeStartKey = key;
                    // 当前魔方为编辑状态
                    this.cubeEdit = true;
                } else {
                    // 将编辑临时数组进行排序,进行结束画框标记计算
                    let keys = this.currentCubeKey.sort((a, b) => a - b);
                    // 取出开始位置的坐标
                    const start = this.untieKey(keys[0]);
                    // 取出结束位置的坐标
                    const end = this.untieKey(keys[keys.length - 1]);
                    // 计算完成的当前选中魔方块
                    const temp = {
                        x: start.x,
                        y: start.y,
                        height: end.y - start.y + 1,
                        width: end.x - start.x + 1,
                        url: '',
                        link: '',
                        linkname: ''
                    }
                    // 合并数据
                    const result = [...this.item.diyCubeResult, temp];
                    this.item.diyCubeResult = result;
                    // console.log(this.item.diyCubeResult);
                    // 改变生成的魔方数据选中下标
                    this.currentCubeIndex = this.item.diyCubeResult.length - 1;
                    // 清除临时数据,方便下次进行计算
                    this.currentCubeKey = []
                    // 将魔方改为正常状态
                    this.cubeEdit = false;
                }
            },
            // 魔方默认块的移入事件
            mouseoverDefaultMagic(item2) {
                // console.log(item2);
                // 如果当前不是编辑状态,则直接返回
                if (!this.cubeEdit) return// 定义一个空数组,用于存储当前魔方块的key
                const keys = [];
                // 取出当前魔方块的首位数据
                const start = this.untieKey(this.currentCubeStartKey)
                // 取出当前魔方块的结尾数据
                const end = { ...item2 }
                // 处理魔方块的y和x,从小到大,方便后续计算、追加数据
                const ys = [start.y, end.y].sort()
                const xs = [start.x, end.x].sort()
​
                // 判定是否重叠
                if (this.antiCollision(start, end)) {
                    return;
                }
​
                for (let i = ys[0]; i <= ys[1]; i++) {
                    for (let j = xs[0]; j <= xs[1]; j++) {
                        keys.push(this.mergeKey(i, j))
                    }
                }
​
                this.currentCubeKey = keys
            },
            antiCollision(start, end) {
                // 有可能从右边从左边选,导致 start.x > end.y,所以要排序
                // 保证 rec1 [x1,y1,x2,y2] (x1,y1)为左上角坐标,(x2,y2)为右下角坐标
                const xs = [start.x, end.x].sort();
                const ys = [start.y, end.y].sort();
                // 因为 end 记录的是 li 块的左上角坐标,构成矩形区域的话,应该算 li 块右下角坐标,所以 + 1
                const rec1 = [xs[0], ys[0], xs[1] + 1, ys[1] + 1]
                // console.log(rec1, 'rec1');
                for (let i = 0; i < this.item.diyCubeResult.length; i++) {
                    const item = this.item.diyCubeResult[i];
                    const rec2 = [item.x, item.y, item.x + item.width, item.y + item.height]
                    // console.log(rec2, 'rec2');
                    const isIntersect = this.isIntersect(rec1, rec2);
                    if (isIntersect) return true
                }
                return false
            },
            // 切换生成的代码块选中状态
            clickDiyMagic(index) {
                if (this.currentCubeIndex == index) return
                this.currentCubeIndex = index
            },
            // 移除选中的方框
            removeDiyMagic(index) {
                this.item.diyCubeResult.splice(index, 1)
                if (this.item.diyCubeResult.length === 0) {
                    this.currentCubeIndex = -1
                } else {
                    this.currentCubeIndex = this.item.diyCubeResult.length - 1
                }
            },
            // 边界处理
            handleMouseLeave() {
                this.currentCubeKey = []
                this.cubeEdit = false;
            },
            //  ****************** 方法库
            // 核心判定两个矩形是否重叠
            isIntersect(rec1, rec2) {
                /**
                 * 核心
                 * 思想:两矩形右边界的最小值大于两矩形左边界的最大值,说明两重叠,两矩形下边界的最小值大于两矩形上边界的最大值,说明两重叠
                */
                return (Math.min(rec1[2], rec2[2]) > Math.max(rec1[0], rec2[0]) &&
                    Math.min(rec1[3], rec2[3]) > Math.max(rec1[1], rec2[1]));
            },
            // 获取当前块的唯一key值
            mergeKey(y, x) {
                return Number(y + (x * 10));
            },
            // 解析 key 值
            untieKey(key) {
                if (key >= 10) {
                    return { x: Math.floor((key % 100) / 10), y: key % 10 };
                } else {
                    return { x: 0, y: key };
                }
            },
            // 选择完成的魔方块的样式
            getStyle(item) {
                const { x, y, width, height } = item;
                let style = {
                    left: `${x * this.itemWidth}px`,
                    top: `${y * this.itemWidth}px`,
                    width: `${width * this.itemWidth}px`,
                    height: `${height * this.itemWidth}px`
                }
                return style
            }
        }
    }
</script><style lang="scss" scoped>
    .dialog-box {
        padding: 20px;
        display: grid;
        grid-template-columns: repeat(3, calc(33.33% - 20px));
        grid-gap: 20px;
        max-height: 500px;
        overflow: auto;
​
        .img-item-box {
            text-align: center;
​
            .img-item {
                background-color: #f7f8fa;
                padding: 10px;
                box-sizing: border-box;
                margin-bottom: 10px;
                border: 1px solid #eee;
                cursor: pointer;
                width: 260px;
                height: 200px;
​
                &.active {
                    border-color: var(--primary);
                }
            }
        }
    }
​
    .clearbox {
        &::after {
            content: '';
            display: block;
            clear: both;
        }
    }
​
    .magic-cube {
        .magic-box {
            position: relative;
            margin: 16px auto;
            display: block !important;
​
            .diy-magic-item {
                position: absolute;
                background-color: #e8f7fd;
                border: 1px solid #bdf;
                box-sizing: border-box;
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
​
                i {
                    position: absolute;
                    font-size: 20px;
                    right: -10px;
                    top: -10px;
                    color: #bbb;
                    display: none;
                    z-index: 999
                }
​
                img {
                    height: 100%;
                    width: 100%;
                    vertical-align: middle;
                    object-fit: cover;
                }
​
                &.active {
                    border-color: var(--primary);
​
                    i {
                        display: block;
                    }
                }
            }
​
            .defaule-row {
                display: flex;
​
                &:first-child {
                    .defaule-col {
                        border-top: 1px solid #e5e5e5;
                    }
                }
​
                .defaule-col {
                    border-left: 1px solid #e5e5e5;
                    border-bottom: 1px solid #e5e5e5;
                    cursor: pointer;
                    background: #f8f8f8;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    box-sizing: border-box;
​
                    &.item-seleting {
                        background-color: #e8f7fd;
​
                        i {
                            display: none;
                        }
                    }
​
                    i {
                        color: #bbb;
                        pointer-events: none;
                    }
​
                    &:last-child {
                        border-right: 1px solid #e5e5e5;
                    }
                }
            }
        }
    }
</style>
// 中间画板的展现,移动端代码相同,注意 rpx 和 px 的换算
<template>
    <div class="middle-magic-cube component-top-position" :style="{
      backgroundColor: item.comp_bg,
      padding: `${item.padding_top}px 0 ${item.padding_bottom}px`
    }">
        <div class="cube-box" :style="{
            margin: `0 ${item.padding_left_right}px`,
            height: boxHeight
        }">
            <div class="diy-magic-item" v-for="(it,idx) in item.diyCubeResult" :key="idx" :style="getStyle(it)">
                <div class="diy-magic-item-inner" :style="{
                    backgroundImage: `url(${it.url})`,
                    borderRadius: `${item.top_radius}px ${item.top_radius}px ${item.bottom_radius}px ${item.bottom_radius}px`,}">
                </div>
            </div>
        </div>
    </div>
</template><script>
export default {
    props: ['item'],
    computed: {
        itemWidth() {
            return (375 - this.item.padding_left_right * 2) / (this.item.diyCubeNumber || 2);
        },
        // 这里主要是处理,风格十一,我只使用了上半部分,下半部分不需要展示
        boxHeight() {
            if (this.item.comp_style === 11) {
                // 取纵轴最大y
                let y = 0;
                this.item.diyCubeResult.forEach(element => y = Math.max(y, (element.y + element.height)));
                return `${this.itemWidth * y}px`
            } else {
                return `${375 - (this.item.padding_left_right * 2 - 1)}px`
            }
        }
    },
    methods: {
        handleActive() {
            this.$eventBus.$emit("on_component_active", this.item);
        },
        // 选择完成的魔方块的样式
        getStyle(item) {
            const { x, y, width, height } = item;
            let style = {
                left: `${x * this.itemWidth}px`,
                top: `${y * this.itemWidth}px`,
                width: `${width * this.itemWidth}px`,
                height: `${height * this.itemWidth}px`,
                padding: `${this.item.image_space / 2}px`,
            }
            return style
        }
    }
}
</script><style lang="scss" scoped>
.middle-magic-cube {
    height: 100%;
    width: 100%;
    box-sizing: border-box;
}
​
.cube-box {
    position: relative;
    height: 375px;
    max-width: 375px;
    overflow: hidden;
​
    .diy-magic-item {
        position: absolute;
        box-sizing: border-box;
​
        .diy-magic-item-inner {
            width: 100%;
            height: 100%;
            background-repeat: no-repeat;
            background-size: cover;
            background-position: center;
        }
​
    }
}
</style>
// 固定风格 1-10 的基本数据
export default {
    magic_1: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 1,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_2: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 0,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_3: {
        diyCubeNumber: 3,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 3,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 0,
            height: 3,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 2,
            y: 0,
            height: 3,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_4: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 0,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_5: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 1,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_6: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 0,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_7: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_8: {
        diyCubeNumber: 2,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 0,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 1,
            y: 1,
            height: 1,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_9: {
        diyCubeNumber: 6,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 3,
            width: 3,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 3,
            y: 0,
            height: 3,
            width: 3,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 0,
            y: 3,
            height: 3,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 2,
            y: 3,
            height: 3,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 4,
            y: 3,
            height: 3,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }]
    }, magic_10: {
        diyCubeNumber: 4,
        diyCubeResult: [{
            x: 0,
            y: 0,
            height: 4,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 2,
            y: 0,
            height: 2,
            width: 2,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 2,
            y: 2,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }, {
            x: 3,
            y: 2,
            height: 2,
            width: 1,
            url: '',
            link: '',
            linkname: ''
        }]
    }
}