You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

557 lines
21 KiB
JavaScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

(function () {
let main = {
grid: null,
initData: null,
};
let welcome = {
grid: null,
initData: null,
};
let currentComponent = null;
let cellHeight = 60;
// 让gridstack知道如何渲染组件children中的content直接渲染html
GridStack.renderCB = function (el, w) {
el.innerHTML = w.content;
};
GridStack.setupDragIn('#components-panel .component-item', undefined, [
{
w: 2,
h: 2,
type: 'image',
name: '图片',
background: '#fff',
image: '',
eventsType: 'click',
eventsAction: '',
defaultFocus: false,
leftId: '',
rightId: '',
upId: '',
downId: '',
focusedStyle_background: '',
focusedStyle_border_width: 0,
focusedStyle_border_color: '',
focusedStyle_scale: 1,
},
{
w: 2,
h: 1,
type: 'text',
childrenType: '',
name: '文本',
background: '#fff',
fontSize: 14,
color: '#333',
text: '文本',
fontWeight: 'normal',
eventsType: 'click',
eventsAction: '',
defaultFocus: false,
leftId: '',
rightId: '',
upId: '',
downId: '',
focusedStyle_background: '',
focusedStyle_border_width: 0,
focusedStyle_border_color: '',
focusedStyle_scale: 1,
},
]);
// 生成唯一id
var generateUniqueId = function () {
const timestamp = Date.now().toString(36);
const randomNum = Math.random().toString(36).substring(2);
return timestamp + randomNum;
};
/** 初始化 */
var init = function (type, self) {
// debugger;
const initDataStorage = localStorage.getItem(`${type}Data`);
if (!initDataStorage) {
self['initData'] = {
id: `grid_${generateUniqueId()}`,
children: [],
version: '1.0.0',
width: 1920,
height: 1080,
background: '#e2e2e2',
image: '', // 为画布添加image属性
};
} else {
self['initData'] = JSON.parse(initDataStorage);
conputedInitData('init', self);
}
console.log(self['initData'], `${type}Data`);
// 初始化时应用画布背景
const canvasId = `${type}-screen`;
if (self.initData.image) {
$('#' + canvasId)
.css('background-image', `url(${self.initData.image})`)
.css('background-size', 'cover');
} else if (self.initData.background) {
$('#' + canvasId).css('background-color', self.initData.background);
}
self['grid'] = GridStack.init(
{
// 一行高度
cellHeight,
// 间距
margin: 0,
minRow: Math.floor(1080 / 2 / cellHeight),
maxRow: Math.floor(1080 / 2 / cellHeight),
acceptWidgets: true,
float: true,
removable: '#trash',
// subGridOpts: subOptions,
// subGridDynamic: true,
children: self['initData'].children,
},
`#${type}-screen`
);
console.log(self['grid'], `${type}.grid实例`);
// grid.load(initData.children);
self['grid'].engine.nodes.forEach((item) => {
handleAddComponent(item, self);
});
self['grid'].on('added', function (_event, itemArray) {
console.log(itemArray, '这里触发了添加了added事件');
itemArray.forEach((item) => {
handleAddComponent(item, self);
});
});
self['grid'].on('removed', function (_event, itemArray) {
console.log(itemArray, '这里触发了移除removed事件');
itemArray.forEach((item) => {
handleRemoveComponent(item);
});
});
};
// 处理添加组件之后的操作
var handleAddComponent = (component, self) => {
if (!component.id) {
component.id = `${component.type}_${generateUniqueId()}`;
}
setComponentView(component);
component.el.addEventListener('click', () => {
console.log('点击组件', component);
if (currentComponent && currentComponent.id === component.id) {
return;
}
// 1. 解绑可能存在的画布属性事件处理器
$('#props-panel form').find('#image, #background').off();
// 2. 重新绑定通用的组件事件处理器
bindComponentEvents();
// 3. 重置属性面板标题并显示所有表单项,为显示组件属性做准备
$('#props-panel .panel-title span').text('组件属性');
$('#props-panel form .form-item').show();
// 清除之前选中组件的获取焦点后的样式
clearOldFocusStyle();
currentComponent = component;
// 设置当前选中组件的获取焦点后的样式
setCurrentFocusStyle();
// 右侧显示组件属性列表
if (!$('#props-panel').find('form').is(':visible')) {
$('#props-panel').find('.wait-box').hide();
$('#props-panel').find('form').show();
}
// 将当前选中组件的属性显示在右侧列表中
setCurrentComponentProps(currentComponent);
// 设置上下左右移动组件ID
setMoveComponentId(currentComponent, self);
});
};
var clearOldFocusStyle = function () {
if (currentComponent) {
let el = $(currentComponent.el).find('.grid-stack-item-content');
el.css('background', '');
if (currentComponent.image) {
el.find('img').attr('src', currentComponent.image);
}
if (currentComponent.background) {
el.css('background', currentComponent.background);
}
el.css('border-color', '');
el.css('border-width', '');
el.css('transform', '');
}
};
var setCurrentFocusStyle = function () {
if (currentComponent) {
let el = $(currentComponent.el).find('.grid-stack-item-content');
if (currentComponent.focusedStyle_background) {
el.css('background', currentComponent.focusedStyle_background);
}
el.css('border-color', currentComponent.focusedStyle_border_color);
el.css('border-width', currentComponent.focusedStyle_border_width + 'px');
el.css('transform', `scale(${currentComponent.focusedStyle_scale})`);
}
};
// 设置上下左右移动组件ID
var setMoveComponentId = function (component, self) {
const allComponents = self['grid'].save();
if (allComponents.length <= 1) {
return;
}
setMoveComponentHtml('leftId', component, allComponents);
setMoveComponentHtml('rightId', component, allComponents);
setMoveComponentHtml('upId', component, allComponents);
setMoveComponentHtml('downId', component, allComponents);
};
var setMoveComponentHtml = function (idName, component, allComponents) {
let componentsHtml = [{ value: '', text: '请选择' }];
allComponents
.filter((item) => item.id !== component.id)
.map((item) => {
componentsHtml.push({ value: item.id, text: `${item.name}(${item.id})`, selected: item.id === component[idName] });
});
let el = $(`#${idName}`);
el.empty();
componentsHtml.forEach((item) => {
el.append(
$('<option>', {
value: item.value,
text: item.text,
selected: item.selected || false,
})
);
});
};
var handleRemoveComponent = (component) => {
if (currentComponent && currentComponent.id === component.id) {
currentComponent = null;
// 右侧显示请选择组件
if (!$('#props-panel').find('wait-box').is(':visible')) {
$('#props-panel').find('.wait-box').show();
$('#props-panel').find('form').hide();
}
}
};
//将当前选中组件的属性显示在右侧列表中
var setCurrentComponentProps = function (component) {
const form = $('#props-panel').find('form');
if (component.hasOwnProperty('childrenType')) {
form.find('#childrenType').val(component.childrenType);
form.find('#childrenType').parent().show();
} else {
form.find('#childrenType').parent().hide();
}
form.find('#name').val(component.name);
if (component.hasOwnProperty('image')) {
form.find('#image').val(component.image);
form.find('#image').parent().show();
} else {
form.find('#image').parent().hide();
}
if (component.hasOwnProperty('background')) {
form.find('#background').val(component.background);
form.find('#background').parent().show();
} else {
form.find('#background').parent().hide();
}
if (component.type === 'text') {
form.find('#text').val(component.text);
form.find('#fontSize').val(component.fontSize);
form.find('#color').val(component.color);
form.find('#fontWeight').val(component.fontWeight);
form.find('#text').parent().show();
form.find('#fontSize').parent().show();
form.find('#color').parent().show();
form.find('#fontWeight').parent().show();
} else {
form.find('#text').parent().hide();
form.find('#fontSize').parent().hide();
form.find('#color').parent().hide();
form.find('#fontWeight').parent().hide();
}
form.find('#eventsType').val(component.eventsType);
form.find('#eventsAction').val(component.eventsAction);
form.find('#defaultFocus').val(component.defaultFocus);
form.find('#leftId').val(component.leftId);
form.find('#rightId').val(component.rightId);
form.find('#upId').val(component.upId);
form.find('#downId').val(component.downId);
form.find('#focusedStyle_background').val(component.focusedStyle_background);
form.find('#focusedStyle_border_width').val(component.focusedStyle_border_width);
form.find('#focusedStyle_border_color').val(component.focusedStyle_border_color);
form.find('#focusedStyle_scale').val(component.focusedStyle_scale);
};
// 定义组件设置配置策略
const componentStrategies = {
background: function (el, value) {
if (!value) {
el.find('.grid-stack-item-content').css('background', 'none');
} else {
el.find('.grid-stack-item-content').css('background', value);
}
},
image: function (el, value, component) {
if (!value) return;
// el.find('.grid-stack-item-content').css('background', `url(${value}) no-repeat center center`).css('background-size', 'cover');
el.find('img').attr('src', value);
},
fontSize: function (el, value) {
if (!value) return;
el.find('span').css('font-size', value + 'px');
},
color: function (el, value) {
if (!value) return;
el.find('span').css('color', value);
},
text: function (el, value) {
if (!value) return;
el.find('span').text(value);
},
fontWeight: function (el, value) {
if (!value) return;
el.find('span').css('font-weight', value);
},
focusedStyle_background: function (el, value, component) {
if (!value) return;
if (currentComponent && currentComponent.id === component.id) {
el.find('.grid-stack-item-content').css('background', value);
}
},
focusedStyle_border_width: function (el, value) {
if (!value) return;
if (currentComponent && currentComponent.id === component.id) {
el.find('.grid-stack-item-content').css('border-width', value + 'px');
}
},
focusedStyle_border_color: function (el, value) {
if (!value) return;
if (currentComponent && currentComponent.id === component.id) {
el.find('.grid-stack-item-content').css('border-color', value);
}
},
focusedStyle_scale: function (el, value) {
if (!value) return;
if (currentComponent && currentComponent.id === component.id) {
el.find('.grid-stack-item-content').css('transform', `scale(${value})`);
}
},
};
// 设置当前传递组件的视图展示
var setComponentView = function (component, fields = null) {
const el = $(component.el);
// 确定要处理的属性
const propsToHandle = fields || Object.keys(componentStrategies);
// 遍历并执行对应的策略
propsToHandle.forEach((prop) => {
if (componentStrategies[prop] && component[prop] !== undefined) {
componentStrategies[prop](el, component[prop], component);
}
});
};
// 定义映射关系元素ID -> 事件类型 -> 组件属性
const elementMappings = {
name: { event: 'blur', property: 'name' },
childrenType: { event: 'blur', property: 'childrenType' },
background: { event: 'blur', property: 'background' },
image: { event: 'change', property: 'image' },
fontSize: { event: 'blur', property: 'fontSize' },
color: { event: 'blur', property: 'color' },
text: { event: 'blur', property: 'text' },
fontWeight: { event: 'blur', property: 'fontWeight' },
eventsType: { event: 'blur', property: 'eventsType' },
eventsAction: { event: 'blur', property: 'eventsAction' },
defaultFocus: { event: 'blur', property: 'defaultFocus' },
leftId: { event: 'change', property: 'leftId' },
rightId: { event: 'change', property: 'rightId' },
upId: { event: 'change', property: 'upId' },
downId: { event: 'change', property: 'downId' },
focusedStyle_background: { event: 'blur', property: 'focusedStyle_background' },
focusedStyle_border_width: { event: 'blur', property: 'focusedStyle_border_width' },
focusedStyle_border_color: { event: 'blur', property: 'focusedStyle_border_color' },
focusedStyle_scale: { event: 'blur', property: 'focusedStyle_scale' },
};
// 统一绑定函数
var bindComponentEvents = function () {
Object.keys(elementMappings).forEach((elementId) => {
const mapping = elementMappings[elementId];
const $element = $('#' + elementId);
if ($element.length) {
// 组件名称添加失去焦点事件或者change事件
$element.off(mapping.event).on(mapping.event, function () {
const value = $(this).val();
currentComponent[mapping.property] = value;
setComponentView(currentComponent, [mapping.property]);
});
}
});
};
// 处理Tab切换的逻辑
var handleTabSwitch = function () {
$('#canvas-tabs .tab-button').on('click', function () {
var $this = $(this);
// 如果已经是激活状态,则不执行任何操作
if ($this.hasClass('active')) {
return;
}
// 处理按钮的激活状态
$this.siblings().removeClass('active');
$this.addClass('active');
// 处理画布的显示/隐藏
var tabId = $this.data('tab');
$('#' + tabId).show();
$('.grid-stack')
.not('#' + tabId)
.hide();
});
};
// 保存的时候计算x,y,w,h
var conputedInitData = function (type, self) {
if (type === 'save') {
let initDataCopy = JSON.parse(JSON.stringify(self['initData']));
initDataCopy.children.forEach((item) => {
item.xCopy = item.x;
item.x = item.x * (1920 / 12);
item.yCopy = item.y;
item.y = item.y * cellHeight * 2;
item.wCopy = item.w;
item.w = item.w * (1920 / 12);
if (!item.hasOwnProperty('h')) {
item.h = 1;
}
item.hCopy = item.h;
item.h = item.h * cellHeight * 2;
item.fontSize = item.fontSize ? parseInt(item.fontSize) : item.fontSize;
});
console.log(initDataCopy);
return initDataCopy;
}
if (type === 'init') {
self['initData'].children.forEach((item) => {
item.x = item.xCopy;
item.y = item.yCopy;
item.w = item.wCopy;
item.h = item.hCopy;
});
}
};
/** 执行方法 */
$(function () {
init('main', main);
init('welcome', welcome);
// 调用绑定
bindComponentEvents();
// 处理Tab切换
handleTabSwitch();
$('.save-container').click(function () {
main.initData.children = main.grid.save();
welcome.initData.children = welcome.grid.save();
console.log(main.initData);
console.log(welcome.initData);
localStorage.setItem('mainData', JSON.stringify(conputedInitData('save', main)));
localStorage.setItem('welcomeData', JSON.stringify(conputedInitData('save', welcome)));
});
// 给画布添加点击事件,用于编辑画布属性
$('#main-screen, #welcome-screen').on('click', function (e) {
// 确保点击的是画布背景,而不是某个组件
if (e.target !== this) {
return;
}
console.log('点击了画布背景,编辑画布属性');
// 1. 如果有,则取消当前选中的组件
if (currentComponent) {
clearOldFocusStyle();
currentComponent = null;
}
// 2. 更新右侧面板以显示画布属性
$('#props-panel .panel-title span').text('画布属性');
$('#props-panel .wait-box').hide();
const $form = $('#props-panel form');
$form.show();
// 3. 只显示画布相关的表单项
$form.find('.form-item').hide(); // 首先隐藏所有
$form.find('#image').parent().show(); // 显示背景图片
$form.find('#background').parent().show(); // 显示背景颜色
// 4. 获取当前点击的画布及其数据对象
const canvasId = this.id; // "main-screen" 或 "welcome-screen"
const type = canvasId.split('-')[0]; // "main" 或 "welcome"
const canvasData = type === 'main' ? main : welcome;
// 5. 将当前画布的属性值填充到表单中
$form.find('#image').val(canvasData.initData.image || '');
$form.find('#background').val(canvasData.initData.background || '');
// 6. 为画布属性输入框绑定新的事件
$form.find('#image, #background').off(); // 先解绑旧事件,防止重复绑定
$form.find('#image').on('change', function () {
const imageUrl = $(this).val();
canvasData.initData.image = imageUrl;
if (imageUrl) {
$('#' + canvasId)
.css('background-image', `url(${imageUrl})`)
.css('background-size', 'cover');
// 当设置图片时,清空背景色
$('#' + canvasId).css('background-color', '');
canvasData.initData.background = '';
$form.find('#background').val('');
} else {
// 如果选择的是“请选择”,则只移除背景图片
$('#' + canvasId).css('background-image', 'none');
}
});
$form.find('#background').on('blur', function () {
const color = $(this).val();
canvasData.initData.background = color;
if (color) {
// 当设置颜色时,清空背景图片
$('#' + canvasId).css('background-color', color);
$('#' + canvasId).css('background-image', 'none');
canvasData.initData.image = '';
$form.find('#image').val('');
}
});
});
});
})();