онлайн-адрес(С лестницей будет быстрее)
Визуальный редактор страниц звучит как мечта, не так ли? Давайте сначала взглянем на анимацию!
Прежде чем реализовать эту функцию, я перерыл много информации в Интернете, но в итоге ничего не нашел, а во всевозможных статьях говорилось о моем прежнем «я»!
Итак, в это время вам нужно подумать о том, как этого добиться?
Необходимо учитывать:
- Перетащите реализацию
- Определение структуры данных
- Разделение компонентов
- Ремонтопригодность и расширяемость
ссылка на объект: Вот самая крутая техника, которую я чувствую, давайте объясним детали по порядку! !
Перетащите реализацию
событие перетаскивания
использовать здесьСобытие перетаскивания H5, в основном используется:
dragstart // 开始拖拽一个元素时触发
draggable // 指定可被拖拽的元素
dragend // 当拖拽操作结束时触发
dragover // 当拖拽元素在可释放目标上移动时触发
drop // 当拖拽元素在可释放目标上被释放时触发
Давайте посмотрим, как использовать эти события:
<!-- 拖拽元素列表数据 -->
<script>
// com 为对应的视图组件,在释放区域显示
typeList: {
banner: {
name: '轮播图',
icon: 'el-icon-picture',
com: Banner
},
product: {
name: '商品',
icon: 'el-icon-s-goods',
com: Product
},
images: {
name: '图片',
icon: 'el-icon-picture',
com: Images
},
}
</script>
<!-- 拖拽元素 -->
<ul
@dragstart="dragStart"
@dragend="dragEnd"
>
<li
v-for="(val, key, index) in typeList"
draggable
:data-type="key"
:key="index + 1"
>
<span :class="val.icon"></span>
<p>{{val.name}}</p>
</li>
</ul>
<!-- 释放区域 -->
<div
class="view-content"
@drop="drog"
@dragover="dragOver"
>
</div>
перетащите, чтобы начать
определить переменнуюtype
, который используется для определения типа текущего перетаскиваемого элемента при запуске события перетаскивания, например: продукт, рекламное изображение...
// 拖拽类型
dragStart(e) {
this.type = e.target.dataset.type
}
После определения типа войдите в следующую область выпуска
двигаться в зоне освобождения
В процессе перемещения положение перетаскиваемого элемента необходимо вычислять в режиме реального времени в соответствии с положением мыши.В частности, вы можете прокрутить вниз, чтобы просмотреть эффект анимации!
// 'view-content': 外层盒子的class,直接 push
// 'item': 盒子内部的元素,需计算位置,进行变换操作
dragOver() {
let className = e.target.className
let name = className !== 'view-content' ? 'item' : 'view-content'
// 组件的默认数据
const defaultData = {
type: this.type, // 组件类型
status: 2, // 默认状态
data: [], // 基本数据
options: {} // 其他操作
}
if (name == 'view-content') {
//...
} else if (name == 'item') {
//...
}
}
Обработка границ, расчет углов
Основные переменные:
-
isPush
: Был ли перетаскиваемый элемент перемещен в данные страницы. -
index
: конечное значение индекса перетаскиваемого элемента. -
curIndex
: значение индекса элемента, в котором находится мышь. -
direction
: верхняя/нижняя часть элемента, над которым находится мышь
когдаname=='view-content'
, указывающий, что перетаскиваемый элемент находится во внешней и пустой освобождаемой области, если он не добавлен, напрямуюpush
Просто
if (name == 'view-content') {
if (!this.isPush) {
this.index = this.view.length
this.isPush = true
this.view.push(defaultData)
}
}
когдаname=='item'
, то есть над существующим элементом нужно вычислить положение, вверх/вниз, добавить или переместить
if (name == 'item') {
let target = e.target
let [ y, h, curIndex ] = [ e.offsetY, target.offsetHeight, target.dataset.index ]
let direction = y < (h / 2) // 计算鼠标处于当前元素的位置,来决定拖拽元素的上/下
if (!this.isPush) {
// first
if (direction) {
if (curIndex == 0) {
this.view.unshift(defaultData)
} else {
this.view.splice(curIndex, 0, defaultData)
}
} else {
curIndex = +curIndex + 1
this.view.splice(curIndex, 0, defaultData)
}
} else {
// Moving
if (direction) {
var i = curIndex == 0 ? 0 : curIndex - 1
var result = this.view[i]['status'] == 2
} else {
var i = +curIndex + 1
var result = this.view.length > i && this.view[i]['status'] == 2
}
// 拖拽元素是否需变换位置
if (result) return
const temp = this.view.splice(this.index, 1)
this.view.splice(curIndex, 0, temp[0])
}
this.index = curIndex // 拖拽元素位置
this.isPush = true // 进入则push,即true
}
-
first
:еще нетpush
, то по текущемуindex
иdirection
чтобы определить положение перетаскиваемого элемента -
Moving
:ужеpush
А мобильное состояние, по текущимindex
иdirection
узнать состояние соответствующего значения, является ли он перетаскиваемым элементом, и если даreturn
, иначе изменить положение
в заключении:Получить индекс элемента, в котором находится текущая мышь, а затем вычислить элемент, в котором находится мышь.上半部分
все еще下半部分
, чтобы определить положение перетаскиваемого элемента! ! !
Кусок пирога:
вышеname=='item'
,Event
События должны предотвращать события по умолчанию, избегатьtarget
Для внутреннего элемента позицию нельзя вычислить, а только использовать событие для предотвращения здесь не получится, и я не знаю почему, нужно поставить.item
Все дочерние элементы плюсаpointer-events: none
характеристики!
e.preventDefault()
e.stopPropagation()
.item div{
pointer-events: none;
}
конец перетаскивания
То есть отпустите мышь или покиньте область отпускания, состояние по умолчанию будет восстановлено.
здесьstatus
Что это делает
- Приведенные выше правила расчета используются для определения того, является ли элемент перетаскиваемым.
- Метод отображения страницы, при перетаскивании отображается только имя компонента, а после отпускания восстанавливается нормальное отображаемое содержимое.
// 结束拖拽
dragEnd(e) {
this.$delete(this.view[this.index], 'status')
this.isPush = false
this.type = null
},
// 已放置到指定位置
drog(e) {
e.preventDefault()
e.stopPropagation()
this.dragEnd()
},
Реализация перетаскивания блока контента
Из-за нехватки времени я здесь ленив и использую более совершенный плагин для перетаскивания списка.Vue.Draggable (star 14.2k)
После некоторого исследования его логика связана с перетаскиванием, реализованным выше, и конкретные методы реализации аналогичны Я считаю, что с приведенными выше реальными боевыми случаями вы тоже можете это сделать!
Почему бы тебе не попробовать?
может основываться наVue.Draggable
Как использовать для реализации компонента перетаскивания, который будет использовать (перетаскивание, слот, DOM) и другие операции.
(Есть время позже, я вернусь и упакую один)
Разделение компонентов
Компонент среднего вида и компонент редактирования справа представляют собой набор наборов. Это действительно набор наборов. Это действительно набор наборов!
page=>index
управляет содержимым всей страницы
.
├── components
| ├── Edit ## 右边编辑
| | ├── Info # 基本信息
| | ├── Image # 广告图
| | ├── Product # 商品
| | └── Index # 管理编辑组件的信息
| └── View ## 中间视图
| | ├── Banner # 轮播图
| | ├── Images # 广告图
| | └── Product # 产品列表
└── page
└── index ## 主页面
Для достижения эффекта предварительного просмотра страницы используйте непосредственноcomponents=>View
Следующие компоненты могут использоваться сpage=>index
Используйте тот же метод, не нужно слишком много модифицировать!
Определение структуры данных
Чтобы получить яркую и расширяемую функцию, необходимо определить квалифицированную структуру данных! В то же время он также может определять качество вашего кода!
Конечно, вам все равно придется решать путем собственного обучения и логического мышления!
Вот самый очевидный способ справиться с этим:Заимствуя отношения объектов, передача значения между компонентами должна передаваться только в одном направлении!
view: [
{
type: 'info',
title: '页面标题',
remarks: '页面备注',
backgroundColor: 'red'
},
{
type: 'banner',
data: [
{ url: '1.jpg', name: '轮播图1', link: 'https://轮播图跳转地址.cn' },
{ url: '2.jpg', name: '轮播图2', link: 'https://轮播图跳转地址.cn' }
]
},
{
type: 'images',
data: [
{ url: '1.jpg', name: '广告图1', link: 'https://广告图跳转地址.cn' },
{ url: '2.jpg', name: '广告图2', link: 'https://广告图跳转地址.cn' }
]
},
{
type: 'product',
data: [
{ id: '1', name: '商品1', image: '1.jpg' },
{ id: '2', name: '商品2', image: '2.jpg' }
],
options: {
originalPrice: true, // 划线价
goodRatio: true, // 好评率
volumeStr: false, // 销量数
}
}
]
это массив, массивitem
представляет собой модуль
-
type
: тип модуля -
data
:Основная информация -
options
: другие операции
.... Вы можете обратиться к оригинальным модулям компонентов и расширить их в соответствии с вашими потребностями.
Изменить передачу компонента по значению
При выборе компонента представления поместитеview
указано вitem
Объект передается в качестве параметра компоненту редактирования!
Объект указывает на один и тот же адрес памяти, и существует ссылочное отношение, и разнонаправленное обновление данных может быть достигнуто только путем его изменения один раз!
<section class="r">
<EditForm
:data="props"
v-if="isRight"
></EditForm>
</section>
<script>
// 切换视图组件
selectType(index) {
this.isRight = false
this.props = this.view[index]
this.$nextTick(() => this.isRight = true)
}
</script>
загрузить изображение
Просто на нем есть компонент для загрузки изображений, здесь я делюсь своими навыками использования! !
использоватьElement-ui
Друзья, которые приносят свои компоненты загрузки, приходите сюда (стучите по доске)
Давайте сначала реализуем упрощенную версию:
<!-- 禁用所有默认方法 -->
<el-upload
:http-request="upload"
:show-file-list="false"
multiple
action
>
<img :src="item.url" v-for="(item, index) in list" :key="index">
</el-upload>
<script>
upload(params) {
const file = params.file;
const form = new FormData();
form.append("file", file);
form.append("clientType", "multipart/form-data");
const index = this.imageIndex // 编辑图片的索引
const data = {
url: URL.createObjectURL(file),
form
}
if (index !== null) {
// this.list => 图片集合
this.$set(this.list, index, data)
} else {
this.list.push(data)
}
}
</script>
- Переопределить метод загрузки
- использовать
URL.createObjectURL(file)
Создайте локальный адрес предварительного просмотра - Пучок
form
Сохраните объект и загрузите его при отправке
// 根据上面的代码,使用Promise实现上传功能
const request = []
this.list.forEach(item => {
request.push(
new Promise((resolve, reject) => {
/**
* 上传接口
* 替换原 url
* 删除 form
*/
imageUpload(item.form).then(res => {
item.url = res.data.url
delete item.form
resolve(res)
}).catch(err => {
reject(err)
})
})
)
})
Promise.all(request).then(res => {
// ... submit ...
})
Когда данные будут отправлены на последнем шаге, загрузите все картинки, а затем вызовите интерфейс для отправки данных после завершения загрузки! !
Это самый правильный подход в ситуации, когда отправляется несколько форм!
окончательное резюме
На самом деле это не сложно, основное внимание уделяется планированию структуры данных, обработке взаимодействия компонентов и логическим методам, если реализована основная цель этого шага.
Другие, такие как добавление компонентов, добавление операций и другие операции расширения, остальные проблемы больше не проблема!
Это можно рассматривать только как короткую версию, вы можете оптимизировать, обдумывать и улучшать в соответствии с вашими потребностями и впитывать собственные знания!
По крайней мере, я удовлетворил потребности своей работы, вау, хахахаха~~~