vue3商城页面装修

大致思路

页面布局分为左中右,左边是组件列表,中间是视图列表,右边是组件内容。

父组件app.vue

<template>
  <div class="content">
    <div class="left">
      <left />
    </div>
    <div class="center">
      <center :value="data" :current="current"></center>
    </div>
    <div class="right">
      <right :current="current" @update="update" />
    </div>
  </div>
</template>
<script setup>
import $bus from "./mitt";
import left from "./left.vue";
import center from "./center.vue";
import right from "./right.vue";
import { useStore } from "./store"
import { reactive, ref, watch } from "vue"
const store = useStore();

const data = ref([])
//当前选中的组件
const current = ref({
  id: 0,
  value: {}
})
watch(data, (val) => {
  console.log(val);
})
const update = (e) => {
  let index = data.value.findIndex(item => item.id == e.id)
  data.value[index] = e
}
// 监听左侧组件点击事件  添加组件到中间组件
$bus.on("change", (val) => {
  data.value.push(val)
  current.value = val
});
// 监听中间组件点击事件  高亮当前选中的组件
$bus.on("changeCurent", (val) => {
  current.value = val
});
// 监听右侧组件上传图片事件  修改中间组件数据
$bus.on("changeImg", (val) => {
  let index = data.findIndex(item => item.id == val.id)
  data.value[index].value.src = val.value
});
</script>
<style scoped>
* {
  padding: 0;
  margin: 0;
}

.content {
  display: flex;
}

.left {
  width: 400px;
}

.center {
  width: 400px;
}

.right {
  flex: 1;
  max-width: calc(100% - 800px);
  overflow: hidden;
}
</style>

左边left.vue

<template>
    <div class="container">
        <el-button v-for="i in list" :key="i.id" @click="handitem(i)">{{ i.title }}</el-button>
    </div>
</template>
<script setup>
import $bus from "./mitt";
import { ref } from "vue";
const list = [
    {
        id: 1,
        name: 'tlp1',
        title: "宣传工具"
    }, {
        id: 2,
        name: 'tlp2',
        title: "宫格工具"
    }, {
        id: 3,
        name: 'tlp3',
        title: "图片工具"
    }, {
        id: 4,
        name: 'tlp4',
        title: "轮播工具"
    }, {
        id: 5,
        name: 'tlp5',
        title: "通知工具"
    },
]

const handitem = (item) => {
    // 根据不同组件类型,生成大致相同数据结构的数据
    $bus.emit("change", (() => {
        switch (item.name) {
            case 'tlp1':
                return {
                    name: item.name,
                    value: {
                        src: '',
                        type: "link",
                        src: "https://img.yzcdn.cn/vant/cat.jpeg",
                    },
                    id: new Date().getTime()
                }
            case 'tlp2':
                return {
                    name: item.name,
                    value: {
                        columns: 4,
                        list: [
                            {
                                type: "link",
                                src: "https://img.yzcdn.cn/vant/cat.jpeg",
                            },
                            {
                                type: "link",
                                src: "https://img.yzcdn.cn/vant/cat.jpeg",
                            },
                            {
                                type: "link",
                                src: "https://img.yzcdn.cn/vant/cat.jpeg",
                            },
                            {
                                type: "link",
                                src: "https://img.yzcdn.cn/vant/cat.jpeg",
                            },
                        ]
                    },
                    id: new Date().getTime()
                }
        }
    })());
}
</script>
<style scoped>
.container {
    padding: 30px;
}

.el-button {
    margin: 10px;
}
</style>

中间center.vue

<template>
    <div class="container">
        <div class="mobile">
            <transition-group name="drag" class="list" tag="ul">
                <li v-for="item in props.value" @dragstart="dragstart(item)" :key="item.id"
                    @dragenter="dragenter($event, item.id)" @dragover.prevent draggable>
                    <component :style="{ border: current.id === item.id || currentDrag == item.id ? 'dotted' : '' }"
                        :is="components[item.name]" :value="item.value" @click="handCmp(item)" />
                </li>
            </transition-group>
        </div>
    </div>
</template>
<script setup>
import $bus from "./mitt";

import tlp1 from "./components/tpl1.vue";
import tlp2 from "./components/tpl2.vue";
import tlp3 from "./components/tpl3.vue";
import tlp4 from "./components/tpl4.vue";
import tlp5 from "./components/tpl5.vue";
const components = {
    tlp1,
    tlp2,
    tlp3,
    tlp4,
    tlp5
};

import { ref, defineProps } from 'vue';


const props = defineProps({
    value: {
        type: Array,
        default: () => []
    },
    current: {
        type: Object,
    }
});

/**
 * 当前选中的索引
 */
const currentDrag = ref(0)

/**
 * 处理组件点击事件
 * @param {Object} item - 被点击的组件对象
 */
const handCmp = (item) => {
    $bus.emit('changeCurent', item)
    currentDrag.value = 0
}

/**
 * 拖拽开始事件处理函数
 * @param {number} id - 被拖拽的组件的id
 */
const dragstart = (item) => {
    console.log(item, 'dragstart');
    $bus.emit('changeCurent', item)
    currentDrag.value = item.id
}

/**
 * 拖拽进入事件处理函数
 * @param {Event} e - 拖拽事件对象
 * @param {number} id - 当前拖拽进入的组件的id
 */
const dragenter = (e, id) => {
    console.log(id, 'dragenter');
    e.dataTransfer.dropEffect = "move";
    e.preventDefault();
    if (currentDrag.value !== id) {
        let index1 = props.value.findIndex(item => item.id == currentDrag.value)
        let index2 = props.value.findIndex(item => item.id == id)
        let temp = props.value[index1]
        props.value[index1] = props.value[index2]
        props.value[index2] = temp
    }
}
</script>
<style scoped>
.container {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    /* 问题图片不可选中 */
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

.mobile {
    width: 375px;
    height: 667px;
    border: solid;
    border-radius: 10px;
    overflow-y: auto;
}
</style>

右边

<template>
    <div class="container">
        {{ props.current }}
        <div v-if="props.current.name == 'tlp1'">
            <input type="file" @change="uploadImg">
            <img :src="props.current.value.src" v-if="props.current.value.src">
            <el-radio-group v-model="props.current.value.type" class="ml-4">
                <el-radio label="link" size="large">页面跳转</el-radio>
                <el-radio label="waibu" size="large">外部链接</el-radio>
            </el-radio-group>
        </div>
        <div v-else-if="props.current.name == 'tlp2'">
            <div class="flex-row">
                <div>列数</div>
                <el-input-number v-model="props.current.value.columns" :min="1" :max="6" :step="1" />
            </div>
            <div v-for="item in props.current.value.list">
                <el-avatar :size="30" :src="item.src" />
            </div>
            <el-button @click="pushGrid">添加</el-button>
        </div>
    </div>
</template>
<script setup>
import { defineProps, watch, defineEmits } from 'vue';
const props = defineProps({
    current: {
        type: Object,
        default: () => { }
    }
});

const emit = defineEmits(['update'])
const uploadImg = (e) => {
    // 获取文件对象
    let file = e.target.files[0];
    // 创建文件读取对象
    let reader = new FileReader();
    // 读取文件
    reader.readAsDataURL(file);
    // 文件读取完毕后触发
    reader.onload = function (e) {
        // 获取文件读取完毕时的结果
        let img = e.target.result;
        emit('update', props.current)
    }
}
const pushGrid = () => {
    // 将链接信息添加到列表中
    props.current.value.list.push({
        type: "link",
        src: "https://img.yzcdn.cn/vant/cat.jpeg",
    })
    emit('update', props.current)
}




</script>
<style scoped>
img {
    width: 200px;
    height: 150px;
}
</style>

总结。始终是要围着app.vue父组件进行的。必须要定义当前点击的组件,不然右侧无法渲染哪个组件的内容。右侧组件无论改了什么,都应该更新值到父组件。父组件通过id来判断哪个组件改变了来更新值。

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值