Слияние кода завершено, страница обновится автоматически
// ID生成器: xf-序列号-最多4位随机数
class IdGen {
constructor() {
this.i = 0;
}
next() {
this.i += 1;
return this.i;
}
static _gen = new IdGen();
static next() {
return 'xf-'+IdGen._gen.next() + "-" + Math.floor(Math.random() * 10000);
}
}
// 组件(对应界面左边的各种小组件),所有组件都有列宽属性,用于控制col-*
class Widget {
constructor(type) {
this.type = type;
}
// 获取代码模板(拖放到设计器的时候要插入这段代码)
get codeTemplate() {
if (!this.type) return "EMPTY"; // _EMPTY
return $(".code-template .frag-" + this.type).html();
}
// 获取属性设置Form(显示到右边的'字段'属性面板中)
get propertyForm() {
if (!this.type) return ""; // _EMPTY
return $(".widget-properties .prop-" + this.type).html();
}
// 子类需要覆盖 updateUI 方法,更新DesignPanel中的组件UI
updateUI(elm,name,value){
console.log('updateUI', arguments)
if('col'==name){
this._updateCol(elm,value);
}
DesignPanel.get().update();
}
//更新组件所占的列宽(bootstrap默认为12列宽的栅格布局)
_updateCol(elm,col){
for(let i=1; i<=12; i++){
elm.removeClass('col-' + i);
}
elm.addClass('col-' + col);
}
//获取所占列宽
_getCol(elm){
for(let i=1; i<=12; i++){
if(elm.hasClass('col-' + i)){
return i;
}
}
//默认返回12
return 12;
}
// 子类需要覆盖 props 方法,获取当前组件的所有属性
props(elm){
return {col: this._getCol(elm)};
}
static _EMPTY = new Widget();
static of(type) {
return Widget._allWidgets[type] || Widget._EMPTY;
}
}
//bootstrap容器组件,可以设置子元素的默认列宽
class WidgetGrid extends Widget {
constructor(type) {
super(type || 'grid'); // 调用超类构造函数并传入name参数
}
addChild(gridElm, childElm){
let p = this.props(gridElm);
super.updateUI(childElm,col, p.defaultCol);
}
//注意gridElm参数,在向设计器添加第一个顶级Grid的时候,gridElm为空
addChildType(gridElm, dropTargetElm, childWidgetType){
let gridProps = gridElm ? this.props(gridElm) : {defaultCol:12};
// console.log('grid props', gridProps);
//赋值一个临时ID,继承GridElm的默认列宽
let elm = $(`<div class='ui-state-default col-${gridProps.defaultCol}' data-id="${IdGen.next()}"></div>`).appendTo(dropTargetElm);
elm.html(Widget.of(childWidgetType).codeTemplate);
// 统一增加遮罩和删除按钮,点击后会显示出来
if('grid'==childWidgetType){
//容器类的组件不能用form-widget-mask的方式来处理,因为里面的小组件还要可以拖拽才行!!!
if(gridElm){
$(".layout-handler", elm).append(
'<span class="badge badge-danger btn-delete my-2"><i class="bi-trash"></i></span>'
);
}
}else{
$(".form-widget:first-child", elm).before(
'<div class="form-widget-mask"><span class="badge badge-danger btn-delete"><i class="bi-trash"></i></span></div>'
);
}
return elm;
}
// 子类需要覆盖 props 方法,获取当前组件的所有属性
props(elm){
let p = super.props(elm);
p.defaultCol=elm.attr('data-default-col') || 12;
return p;
}
updateUI(elm,name,value){
super.updateUI(elm,name,value);
if('defaultCol'==name){
elm.attr('data-default-col',value*1);
console.log('set defaultCol', value, 'get:', this.props(elm));
}
}
}
class WidgetInput extends Widget {
constructor(type) {
super(type || 'input'); // 调用超类构造函数并传入name参数
}
updateUI(elm,name,value){
console.log('widget [input] updateUI',arguments);
if('label'==name){
$('label',elm).html(value);
}else if('placeholder'==name){
this.coreElm(elm).attr('placeholder',value);
}
super.updateUI(elm,name,value);
}
//返回Input组件的核心元素<input> or <textarea>
coreElm(elm){
return $(`${this.type}.form-control`,elm);
}
props(elm){
let p = super.props(elm);
p.label = $('label',elm).html();
p.placeholder=this.coreElm(elm).attr('placeholder');
return p;
}
}
class WidgetTextArea extends WidgetInput {
constructor(type) {
super(type || 'textarea'); // 调用超类构造函数并传入name参数
}
updateUI(elm,name,value){
let ce = this.coreElm(elm);
if('rows'==name){
ce.attr('rows',value);
}
super.updateUI(elm,name,value);
}
props(elm){
let p = super.props(elm);
let ce = this.coreElm(elm);
p.rows=ce.attr('rows');
return p;
}
}
Widget._allWidgets = {
input: new WidgetInput(),
textarea: new WidgetTextArea(),
grid: new WidgetGrid(),
};
// 表单元素, 即是某一个Widget拖拽到画布上的实例
class FormItem {
constructor(type,id) {
this.type = type;
this.id = id;
}
get widget() {
return Widget.of(this.type);
}
}
// 中间的预览
class Previewer {
constructor(designPanel, selector) {
this.designPanel = designPanel;
this.elm = $(selector);
}
update(html) {
this.elm.html(html);
}
}
// 中间的代码
class Coder {
constructor(designPanel, selector) {
this.designPanel = designPanel;
this.elm = $(selector);
}
update(html) {
this.elm.html(html);
}
}
// 中间的设计器
class Designer {
constructor(designPanel, selector) {
this.designPanel = designPanel;
this.elm = $(selector);
}
//拖拽增加一个组件
dragTo(target, dragger, event) {
console.log('target:', target, 'arguments:',arguments);
let widgetType = dragger.draggable.data("type"); // widget type
let gridElm = $(target).parent().parent();
let childElm = Widget.of('grid').addChildType(gridElm, target,widgetType);
this.bindWidgetEvent(childElm);
makeDroppable($('.widget-form-list',childElm));
this.designPanel.update();
//拖拽过来以后,默认选中
this.onClick(childElm);
}
// 设计区域选择了一个组件,需要在右边显示对应的属性
onClick(target) {
target=$(target);
console.log('click',target);
if (this.lastMask) {
this.lastMask.removeClass("active");
}
let mask = $(".form-widget-mask", target);
mask.addClass("active");
this.lastMask = mask;
// 属性面板需要load一下
PropertyPanel.load(target);
}
// 设计区域点击了删除按钮
onDelete(target) {
if (this.lastMask) {
this.lastMask = false;
}
target.remove();
this.designPanel.update();
}
// 设计区域拖拽了一个新组件进来以后,需要绑定选取事件
bindWidgetEvent(target) {
let that = this;
//绑定点击事件
$(".form-widget-mask, .layout-handler", target).click(function (e) {
that.onClick(target);
e.stopPropagation();
});
//绑定删除按钮
$(".form-widget-mask .btn-delete, .layout-handler .btn-delete", target).click(function (e) {
that.onDelete(target);
e.stopPropagation();
});
//可以排序,排序结束做一下更新
$('.widget-form-list').sortable({
start:(event,ui)=>that.onClick(ui.item[0]),
stop:()=>that.designPanel.update() // 不能用change, change会导致出来的位置不准,因为还没有stop,没有停留到合适的位置
});
}
get html() {
let elm = $("#design-form").clone();
//需要删除多余的遮罩元素
$(".form-widget-mask, .layout-handler", elm).remove();
//删除多余的样式,主要是拖拽组件附加上的各种样式
$(['ui-state-default','ui-sortable-handle','ui-sortable','ui-droppable']).each((i,cls)=>{
$("."+cls, elm).removeClass(cls);
});
let html = elm[0].outerHTML;
return html;
}
static get() {
return DesignPanel.get().designer;
}
static dragTo(target, dragger, event) {
Designer.get().dragTo(target, dragger, event);
}
}
// 中间的设计面板,包括:设计器,预览区,代码
class DesignPanel {
constructor(selector) {
this.elm = $(selector);
this.designer = new Designer(this, $("#widget-form-container", this.elm));
this.previewer = new Previewer(this, $("#preview-body", this.elm));
this.coder = new Coder(this, $("#code-textarea", this.elm));
}
update() {
setTimeout(() => {
let html = this.designer.html;
this.previewer.update(html);
this.coder.update(html);
}, 300);
}
static _panel = new DesignPanel("#design-panel");
static get() {
return DesignPanel._panel;
}
}
class PropertyPanel {
static load(elm){
let widgetType = $(".form-widget", elm).data("type");
let widget = Widget.of(widgetType);
PropertyPanel._elm = elm;
PropertyPanel._widget = widget;
//加载属性Form
$("#prop-tab-panel-field").html(widget.propertyForm);
let props = widget.props(elm);
// console.log('widget props:', props);
//设置各个属性值
$('#prop-tab-panel-field .form-control').each((i,e)=>{
e = $(e);
let name = e.data('field')
e.val(props[name]);
});
$('#prop-tab-panel-field .form-control').keyup(function(event){
let e = $(event.target);
let name = e.data('field')
widget.updateUI(elm,name,e.val());
});
}
static _elm ;
static _widget ;
static all() {
let p = {};
$('#prop-tab-panel-field .form-control').each((i,elm)=>{
elm = $(elm);
p[elm.data('field')]=elm.val();
});
return p;
}
}
function onWindowResize() {
//调整设计区域大小
let parentTop = $("#design-panel .tab-content").offset().top;
let winHeight = $(window).height();
let clientHeight = winHeight - parentTop - 10;
$(".widget-form-container").height(clientHeight);
$("#code-textarea").height(clientHeight);
$("#preview-body").height(clientHeight);
}
let body = $(document).find("body");
//设置为可拖放的区域
function makeDroppable(selector){
$(selector).droppable({
accept: ".form-edit-widget-label",
greedy: true,
classes: {
"ui-droppable-hover": "ui-state-hover",
},
drop: function (event, ui) {
Designer.dragTo($(this), ui, event);
},
});
}
// 页面加载以后的功能初始化
(function () {
"use strict";
// 左边组件区域的拖拽事件
$(".components-list .form-edit-widget-label").draggable({
// helper: "clone",
containment: "window",
cursor: "move",
helper: function (event) {
let target = $(this).clone();
$(target).addClass("dragging-widget");
target.appendTo(body);
return target;
},
revert: "invalid",
});
//调整设计区域大小
onWindowResize();
$(window).resize(function () {
onWindowResize();
});
//默认向设计区增加一个顶级的Grid组件
setTimeout(()=>{
let topGrid = Widget.of('grid').addChildType(undefined, $("#design-form"),'grid');
// 中间设计区域的拖放事件
makeDroppable('.widget-form-list');
Designer.get().bindWidgetEvent(topGrid);
},500)
})();
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )