需求:拖拉拽形成頁面
效果:可定義客戶端和移動(dòng)端基本樣式:柵格,寬高,陰影等础拨,且客戶端與移動(dòng)端樣式互不影響氮块,這里只有大體樣式,并不是最終展示效果
附圖:
image.png
邏輯就不多說了诡宗,就是拖拽
<template>
<div>
<div class="custom_box">
<div class="left">
<div class="title_box">
組件庫
</div>
<div class="main_box">
<ul class="left_ul">
<li
v-for="(item, index) in leftList"
:id="item.type"
:key="item.iden"
class="left_li"
draggable="true"
@dragstart="leftDrag($event, index)"
>
{{ item.name }}
</li>
</ul>
</div>
</div>
<div
class="center"
@drop="contentDrop($event)"
@dragover="contentDragover($event)"
>
<div class="title_box">
<div>
<a-form layout="inline" :label-col="{ span: 5 }">
<a-form-item label="展示端">
<a-select v-model="terminal">
<a-select-option value="pcInfo">
PC端
</a-select-option>
<a-select-option value="mobileInfo">
移動(dòng)端
</a-select-option>
</a-select>
</a-form-item>
</a-form>
</div>
<div>
<a-button type="primary" @click="watchFn('pcInfo')">
預(yù)覽PC
</a-button>
<a-button type="primary" @click="watchFn('mobileInfo')">
預(yù)覽Mobile
</a-button>
</div>
</div>
<div class="content main_box">
<div
v-for="(view, index) in info"
:key="view.iden"
class="content_item"
draggable="true"
@dragenter="enter($event, view)"
@dragstart="handleDragStart($event, view)"
@dragover.prevent="handleDragover($event)"
>
<component
:is="view.type"
:is-disabled="true"
:terminal="terminal"
:view-info="info[index]"
/>
<div
class="mask_box"
:class="view.iden === notContentIden ? 'active' : ''"
@click="editData(view, index)"
>
<a-icon
class="icon_del"
type="delete"
@click="deleteFn(view, index)"
/>
</div>
</div>
</div>
</div>
<div class="right">
<div class="title_box">
組件屬性
</div>
<div class="main_box">
<div class="right_main">
<WriteChildren
v-for="(child, index) in rightList"
:key="index"
:edit-info.sync="editInfo"
:terminal="terminal"
:index="index"
/>
</div>
</div>
</div>
</div>
<div class="dialog">
<a-drawer
title="預(yù)覽"
:visible="visibleModal"
width="100%"
@close="onClose"
>
<div
class="main"
:style="{ width: watchType == 'pcInfo' ? '' : '375px' }"
>
<div
v-for="(view, index) in info"
:key="index"
:style="[
{
width:
view.type != 'JrBanner' && watchType == 'pcInfo'
? '1200px'
: '100%',
},
{ margin: 'auto' },
]"
>
<component
:is="view.type"
:terminal="watchType"
:usage-scenarios="true"
:view-info="info[index]"
/>
</div>
</div>
</a-drawer>
</div>
</div>
</template>
<script>
import { debounce } from "@/libs/utils";
// 編寫
import WriteChildren from "./writeChildren/index";
// 視圖
import JrBanner from "./viewChildren/jrBanner";
import JrCard from "./viewChildren/jrCard";
import JrAdvert from "./viewChildren/jrAdvert";
export default {
name: "Custom",
components: {
// 編寫
WriteChildren,
// 視圖
JrBanner,
JrCard,
JrAdvert,
},
data() {
return {
data: [],
contentList: [],
leftList: [
{
iden: 1,
type: "JrBanner",
name: "輪播",
field_name: "banner",
},
{
iden: 2,
type: "JrCard",
name: "卡片",
},
{
iden: 20,
type: "JrAdvert",
name: "廣告",
},
],
editInfo: {},
info: [],
rightList: [],
terminal: "pcInfo",
checkInfo: {},
notContentIden: null,
visibleModal: false,
watchType: "pcInfo",
};
},
computed: {
formInfo() {
let arr = [];
this.info.map((item) => {
arr.push({
title: item.name,
dataIndex: item.field_name,
});
});
return arr;
},
itemId() {
return this.$route.query.id;
},
},
created() {
this.enter = debounce(this.handleDragEnter, 300);
},
methods: {
leftDrag(ev) {
ev.dataTransfer.setData("Text", ev.target.id);
},
//添加
contentDrop(ev) {
if (this.type) {
this.type = false;
return false;
}
ev.preventDefault();
let newLeftList = JSON.parse(JSON.stringify(this.leftList));
var index = newLeftList.findIndex((item) => {
return item.type === ev.dataTransfer.getData("Text");
});
let data = newLeftList[index];
data.iden = new Date().getTime();
data.show_title = false;
data.fatherCode = ""; // 廣告位code
data.pcInfo = {
height: 300,
count: 3,
bgColor: "",
shadow: true,
alignment: "left",
};
data.mobileInfo = {
height: 300,
count: 2,
bgColor: "",
shadow: true,
alignment: "center",
};
this.notContentIden = data.iden;
data.options = [
// value:值滔蝉,key:鍵,main_text:主文本僚焦,sub_text:副文本锰提,topNumber:距離頂部距離
{ value: "", key: "", main_text: "", sub_text: "", topNumber: 0 },
];
this.editInfo = data;
this.rightList = [];
this.info.push(data);
this.rightList.push(data);
},
contentDragover(ev) {
ev.preventDefault();
},
//編輯/修改
editData(data) {
this.notContentIden = data.iden;
this.rightList = [];
this.rightList.push(data);
this.editInfo = data;
},
/* 換位 */
handleDragStart(ev, data) {
this.type = true;
this.checkInfo = data;
},
handleDragover(ev) {
ev.dataTransfer.dropEffect = "move";
ev.preventDefault();
},
handleDragEnter(ev, data) {
ev.dataTransfer.effectAllowed = "move";
if (data === this.checkInfo) {
return false;
}
const newData = [...this.info];
const src = newData.indexOf(this.checkInfo);
const dst = newData.indexOf(data);
newData.splice(dst, 0, ...newData.splice(src, 1));
this.info = newData;
},
//刪除
deleteFn(data, index) {
this.info.splice(index, 1);
},
//查看/預(yù)覽
watchFn(type) {
this.watchType = type;
this.visibleModal = true;
},
onClose() {
this.visibleModal = false;
},
},
};
</script>