Files
mywebbuilder.js/js/editor/editor.js
2026-03-29 11:50:51 +08:00

310 lines
9.9 KiB
JavaScript

/**
* IoT 可视化编辑器初始化脚本
* 仅支持手机端布局
*/
// 初始化编辑器
function initEditor() {
// 初始化 webbuilder
webbuilder.init('#editor');
// 注册示例组件
registerComponents();
// 绑定事件
bindEvents();
}
/**
* 注册示例组件
*/
function registerComponents() {
// 仪表盘组件
webbuilder.define('gauge', {
type: 'gauge',
label: '仪表盘',
icon: '📊',
defaultColumnSpan: 6,
defaultRowSpan: 6,
defaultProps: {
label: '温度',
value: 25,
unit: '°C'
},
traits: [
{ type: 'text', name: 'label', label: '标签', default: '温度' },
{ type: 'number', name: 'value', label: '值', default: 25 },
{ type: 'text', name: 'unit', label: '单位', default: '°C' }
],
render: function(props) {
var el = document.createElement('div');
el.className = 'iot-gauge';
var valueEl = document.createElement('div');
valueEl.className = 'value';
valueEl.textContent = props.value + props.unit;
var labelEl = document.createElement('div');
labelEl.className = 'label';
labelEl.textContent = props.label;
el.appendChild(valueEl);
el.appendChild(labelEl);
return el;
}
});
// 开关组件
webbuilder.define('switch', {
type: 'switch',
label: '开关',
icon: '🔘',
defaultColumnSpan: 4,
defaultRowSpan: 3,
defaultProps: {
label: '开关',
state: false
},
traits: [
{ type: 'text', name: 'label', label: '标签', default: '开关' },
{ type: 'checkbox', name: 'state', label: '状态', default: false }
],
render: function(props) {
var el = document.createElement('div');
el.className = 'iot-switch';
var switchEl = document.createElement('div');
switchEl.className = 'switch' + (props.state ? ' active' : '');
var labelEl = document.createElement('div');
labelEl.className = 'label';
labelEl.textContent = props.label;
el.appendChild(switchEl);
el.appendChild(labelEl);
return el;
}
});
// 滑块组件
webbuilder.define('slider', {
type: 'slider',
label: '滑块',
icon: '🎚️',
defaultColumnSpan: 8,
defaultRowSpan: 3,
defaultProps: {
label: '亮度',
value: 50,
min: 0,
max: 100
},
traits: [
{ type: 'text', name: 'label', label: '标签', default: '亮度' },
{ type: 'number', name: 'value', label: '值', default: 50 },
{ type: 'number', name: 'min', label: '最小值', default: 0 },
{ type: 'number', name: 'max', label: '最大值', default: 100 }
],
render: function(props) {
var el = document.createElement('div');
el.className = 'iot-slider';
var labelEl = document.createElement('div');
labelEl.className = 'label';
labelEl.textContent = props.label + ': ' + props.value;
var inputEl = document.createElement('input');
inputEl.type = 'range';
inputEl.min = props.min;
inputEl.max = props.max;
inputEl.value = props.value;
el.appendChild(labelEl);
el.appendChild(inputEl);
return el;
}
});
// 按钮组件
webbuilder.define('button', {
type: 'button',
label: '按钮',
icon: '🔘',
defaultColumnSpan: 4,
defaultRowSpan: 2,
defaultProps: {
text: '点击'
},
traits: [
{ type: 'text', name: 'text', label: '文本', default: '点击' }
],
render: function(props) {
var el = document.createElement('div');
el.className = 'iot-button';
el.textContent = props.text;
return el;
}
});
// 数值显示组件
webbuilder.define('value-display', {
type: 'value-display',
label: '数值显示',
icon: '📈',
defaultColumnSpan: 4,
defaultRowSpan: 3,
defaultProps: {
label: '湿度',
value: 65,
unit: '%'
},
traits: [
{ type: 'text', name: 'label', label: '标签', default: '湿度' },
{ type: 'number', name: 'value', label: '值', default: 65 },
{ type: 'text', name: 'unit', label: '单位', default: '%' }
],
render: function(props) {
var el = document.createElement('div');
el.className = 'iot-value-display';
var valueEl = document.createElement('div');
valueEl.className = 'value';
valueEl.textContent = props.value + props.unit;
var labelEl = document.createElement('div');
labelEl.className = 'label';
labelEl.textContent = props.label;
el.appendChild(valueEl);
el.appendChild(labelEl);
return el;
}
});
}
/**
* 绑定事件
*/
function bindEvents() {
// 保存 JSONB
document.getElementById('btn-save').addEventListener('click', function() {
var jsonb = webbuilder.toJSONB();
var jsonStr = JSON.stringify(jsonb, null, 2);
downloadFile('canvas-config.json', jsonStr, 'application/json');
});
// 加载 JSONB
document.getElementById('btn-load').addEventListener('click', function() {
document.getElementById('file-input').click();
});
document.getElementById('file-input').addEventListener('change', function(e) {
var file = e.target.files[0];
if (!file) return;
var reader = new FileReader();
reader.onload = function(e) {
try {
var jsonb = JSON.parse(e.target.result);
webbuilder.fromJSONB(jsonb);
} catch (err) {
alert('加载失败:' + err.message);
}
};
reader.readAsText(file);
// 清空文件输入
this.value = '';
});
// 导出 HTML
document.getElementById('btn-export').addEventListener('click', function() {
// 创建一个临时 div 来渲染
var tempDiv = document.createElement('div');
document.body.appendChild(tempDiv);
// 调用 renderToDiv 获取组件列表
var components = webbuilder.renderToDiv(tempDiv);
// 生成 HTML
var config = webbuilder.getCanvasConfig();
var html = '<!DOCTYPE html>\n<html>\n<head>\n<meta charset="UTF-8">\n';
html += '<meta name="viewport" content="width=device-width, initial-scale=1.0">\n';
html += '<title>IoT 仪表盘</title>\n';
html += '<style>\n';
html += '* { margin: 0; padding: 0; box-sizing: border-box; }\n';
html += 'body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background: #f0f2f5; }\n';
html += '.iot-canvas { width: ' + config.width + 'px; height: ' + config.height + 'px; ';
html += 'display: grid; grid-template-columns: repeat(' + config.columns + ', ' + config.cellSize + 'px); ';
html += 'grid-auto-rows: ' + config.cellSize + 'px; gap: ' + config.gap + 'px; ';
html += 'background: ' + config.background + '; padding: ' + config.gap + 'px; box-sizing: border-box; }\n';
html += '</style>\n</head>\n<body>\n';
html += tempDiv.outerHTML;
html += '\n<script>\n';
html += '// 组件列表(包含每个组件的元素和属性)\n';
html += 'var components = ' + JSON.stringify(components.map(function(c) {
return { id: c.id, type: c.type, props: c.props };
}), null, 2) + ';\n';
html += '</script>\n';
html += '</body>\n</html>';
// 移除临时 div
document.body.removeChild(tempDiv);
showPreview(html);
});
// 清空画布
document.getElementById('btn-clear').addEventListener('click', function() {
if (confirm('确定要清空画布吗?')) {
webbuilder.clear();
}
});
// 预览弹窗关闭
document.querySelector('.modal-close').addEventListener('click', function() {
document.getElementById('preview-modal').style.display = 'none';
});
// 复制代码
document.getElementById('btn-copy').addEventListener('click', function() {
var code = document.getElementById('preview-code').textContent;
navigator.clipboard.writeText(code).then(function() {
alert('已复制到剪贴板');
});
});
// 下载文件
document.getElementById('btn-download').addEventListener('click', function() {
var code = document.getElementById('preview-code').textContent;
downloadFile('page.html', code, 'text/html');
});
}
/**
* 显示预览弹窗
*/
function showPreview(code) {
document.getElementById('preview-code').textContent = code;
document.getElementById('preview-modal').style.display = 'flex';
}
/**
* 下载文件
*/
function downloadFile(filename, content, type) {
var blob = new Blob([content], { type: type });
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', initEditor);