下面是一个简单的低代码前端编辑器的 HTML 实现,包含了基本的拖拽组件、属性编辑和预览功能。
在这里插入图片描述

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>低代码前端编辑器</title>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: Arial, sans-serif;
            display: flex;
            height: 100vh;
            overflow: hidden;
        }
        /* 左侧组件面板 */
        .components-panel {
            width: 200px;
            background-color: #f0f0f0;
            padding: 10px;
            border-right: 1px solid #ccc;
            overflow-y: auto;
        }
        .component-item {
            padding: 8px;
            margin-bottom: 8px;
            background-color: #fff;
            border: 1px solid #ddd;
            border-radius: 4px;
            cursor: grab;
            user-select: none;
        }
        .component-item:hover {
            background-color: #e9e9e9;
        }
        /* 中间画布 */
        .canvas {
            flex: 1;
            padding: 20px;
            background-color: #f9f9f9;
            overflow: auto;
            position: relative;
        }
        .canvas-container {
            min-height: 100%;
            background-color: white;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            position: relative;
        }
        /* 右侧属性面板 */
        .properties-panel {
            width: 250px;
            background-color: #f0f0f0;
            padding: 10px;
            border-left: 1px solid #ccc;
            overflow-y: auto;
        }
        .property-group {
            margin-bottom: 15px;
        }
        .property-group h3 {
            margin-bottom: 8px;
            font-size: 14px;
            color: #555;
        }
        .property-item {
            margin-bottom: 8px;
        }
        .property-item label {
            display: block;
            margin-bottom: 4px;
            font-size: 12px;
            color: #666;
        }
        .property-item input, .property-item select, .property-item textarea {
            width: 100%;
            padding: 6px;
            border: 1px solid #ddd;
            border-radius: 4px;
        }
        /* 顶部工具栏 */
        .toolbar {
            background-color: #333;
            color: white;
            padding: 10px;
            display: flex;
            justify-content: space-between;
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            z-index: 100;
        }
        .toolbar button {
            background-color: #555;
            color: white;
            border: none;
            padding: 6px 12px;
            margin-right: 5px;
            border-radius: 4px;
            cursor: pointer;
        }
        .toolbar button:hover {
            background-color: #666;
        }
        /* 画布上的组件 */
        .canvas-component {
            position: absolute;
            border: 1px dashed #ccc;
            padding: 10px;
            background-color: white;
            cursor: move;
        }
        .canvas-component.selected {
            border: 1px solid #4a90e2;
            background-color: #f0f7ff;
        }
        .resize-handle {
            width: 10px;
            height: 10px;
            background-color: #4a90e2;
            position: absolute;
            right: 0;
            bottom: 0;
            cursor: nwse-resize;
        }
    </style>
</head>
<body>
    <!-- 左侧组件面板 -->
    <div class="components-panel">
        <h2>组件</h2>
        <div class="component-item" draggable="true" data-type="button">按钮</div>
        <div class="component-item" draggable="true" data-type="text">文本</div>
        <div class="component-item" draggable="true" data-type="input">输入框</div>
        <div class="component-item" draggable="true" data-type="image">图片</div>
        <div class="component-item" draggable="true" data-type="container">容器</div>
    </div>

    <!-- 中间画布 -->
    <div class="canvas">
        <div class="toolbar">
            <div>
                <button id="preview-btn">预览</button>
                <button id="save-btn">保存</button>
                <button id="clear-btn">清空</button>
            </div>
            <div>
                <button id="undo-btn">撤销</button>
                <button id="redo-btn">重做</button>
            </div>
        </div>
        <div class="canvas-container" id="canvas-container">
            <!-- 组件将在这里被添加 -->
        </div>
    </div>

    <!-- 右侧属性面板 -->
    <div class="properties-panel">
        <h2>属性</h2>
        <div id="properties-content">
            <p>选择一个组件来编辑其属性</p>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const canvasContainer = document.getElementById('canvas-container');
            const propertiesContent = document.getElementById('properties-content');
            let selectedComponent = null;
            
            // 组件计数器
            let componentCount = 0;
            
            // 默认组件属性
            const componentDefaults = {
                button: {
                    text: '按钮',
                    color: '#ffffff',
                    bgColor: '#4a90e2',
                    width: '100px',
                    height: '40px',
                    fontSize: '14px'
                },
                text: {
                    content: '这是一段文本',
                    color: '#333333',
                    fontSize: '16px',
                    fontWeight: 'normal'
                },
                input: {
                    placeholder: '请输入内容',
                    width: '200px',
                    height: '30px'
                },
                image: {
                    src: 'https://via.placeholder.com/150',
                    width: '150px',
                    height: '150px'
                },
                container: {
                    width: '300px',
                    height: '200px',
                    bgColor: '#f0f0f0'
                }
            };
            
            // 为组件面板中的项目添加拖拽功能
            document.querySelectorAll('.component-item').forEach(item => {
                item.addEventListener('dragstart', function(e) {
                    e.dataTransfer.setData('text/plain', this.getAttribute('data-type'));
                });
            });
            
            // 画布接收拖拽
            canvasContainer.addEventListener('dragover', function(e) {
                e.preventDefault();
            });
            
            canvasContainer.addEventListener('drop', function(e) {
                e.preventDefault();
                const componentType = e.dataTransfer.getData('text/plain');
                addComponentToCanvas(componentType, e.clientX - this.getBoundingClientRect().left, e.clientY - this.getBoundingClientRect().top);
            });
            
            // 添加组件到画布
            function addComponentToCanvas(type, x, y) {
                componentCount++;
                const id = `component-${componentCount}`;
                const component = document.createElement('div');
                component.className = 'canvas-component';
                component.id = id;
                component.style.left = `${x}px`;
                component.style.top = `${y}px`;
                
                // 设置默认属性
                const defaults = componentDefaults[type];
                
                // 根据类型创建不同的内容
                switch(type) {
                    case 'button':
                        component.innerHTML = `<button style="width:${defaults.width};height:${defaults.height};background-color:${defaults.bgColor};color:${defaults.color};font-size:${defaults.fontSize}">${defaults.text}</button>`;
                        component.dataset.props = JSON.stringify(defaults);
                        break;
                    case 'text':
                        component.innerHTML = `<div style="color:${defaults.color};font-size:${defaults.fontSize};font-weight:${defaults.fontWeight}">${defaults.content}</div>`;
                        component.dataset.props = JSON.stringify(defaults);
                        break;
                    case 'input':
                        component.innerHTML = `<input type="text" placeholder="${defaults.placeholder}" style="width:${defaults.width};height:${defaults.height}">`;
                        component.dataset.props = JSON.stringify(defaults);
                        break;
                    case 'image':
                        component.innerHTML = `<img src="${defaults.src}" style="width:${defaults.width};height:${defaults.height}" alt="图片">`;
                        component.dataset.props = JSON.stringify(defaults);
                        break;
                    case 'container':
                        component.style.width = defaults.width;
                        component.style.height = defaults.height;
                        component.style.backgroundColor = defaults.bgColor;
                        component.dataset.props = JSON.stringify(defaults);
                        break;
                }
                
                // 添加调整大小手柄
                const resizeHandle = document.createElement('div');
                resizeHandle.className = 'resize-handle';
                component.appendChild(resizeHandle);
                
                // 点击选择组件
                component.addEventListener('mousedown', function(e) {
                    if (e.target === resizeHandle) return;
                    
                    // 取消之前选择的组件
                    if (selectedComponent) {
                        selectedComponent.classList.remove('selected');
                    }
                    
                    // 选择当前组件
                    selectedComponent = this;
                    this.classList.add('selected');
                    
                    // 显示属性面板
                    showPropertiesPanel(type, JSON.parse(this.dataset.props), id);
                    
                    // 阻止事件冒泡,避免触发画布的点击事件
                    e.stopPropagation();
                });
                
                // 调整大小
                resizeHandle.addEventListener('mousedown', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    
                    const startX = e.clientX;
                    const startY = e.clientY;
                    const startWidth = parseInt(component.style.width) || parseInt(getComputedStyle(component).width);
                    const startHeight = parseInt(component.style.height) || parseInt(getComputedStyle(component).height);
                    
                    function doResize(e) {
                        const newWidth = startWidth + (e.clientX - startX);
                        const newHeight = startHeight + (e.clientY - startY);
                        component.style.width = `${newWidth}px`;
                        component.style.height = `${newHeight}px`;
                        
                        // 更新属性
                        const props = JSON.parse(component.dataset.props);
                        props.width = `${newWidth}px`;
                        props.height = `${newHeight}px`;
                        component.dataset.props = JSON.stringify(props);
                    }
                    
                    function stopResize() {
                        window.removeEventListener('mousemove', doResize);
                        window.removeEventListener('mouseup', stopResize);
                    }
                    
                    window.addEventListener('mousemove', doResize);
                    window.addEventListener('mouseup', stopResize);
                });
                
                // 拖动组件
                component.addEventListener('mousedown', function(e) {
                    if (e.target === resizeHandle) return;
                    
                    const startX = e.clientX;
                    const startY = e.clientY;
                    const startLeft = parseInt(component.style.left) || 0;
                    const startTop = parseInt(component.style.top) || 0;
                    
                    function doDrag(e) {
                        const newLeft = startLeft + (e.clientX - startX);
                        const newTop = startTop + (e.clientY - startY);
                        component.style.left = `${newLeft}px`;
                        component.style.top = `${newTop}px`;
                    }
                    
                    function stopDrag() {
                        window.removeEventListener('mousemove', doDrag);
                        window.removeEventListener('mouseup', stopDrag);
                    }
                    
                    window.addEventListener('mousemove', doDrag);
                    window.addEventListener('mouseup', stopDrag);
                });
                
                canvasContainer.appendChild(component);
                
                // 选择新添加的组件
                component.click();
            }
            
            // 显示属性面板
            function showPropertiesPanel(type, props, id) {
                let html = `<div class="property-group">
                    <h3>${type} 属性</h3>`;
                
                switch(type) {
                    case 'button':
                        html += `
                            <div class="property-item">
                                <label>文本</label>
                                <input type="text" value="${props.text}" data-prop="text">
                            </div>
                            <div class="property-item">
                                <label>宽度</label>
                                <input type="text" value="${props.width}" data-prop="width">
                            </div>
                            <div class="property-item">
                                <label>高度</label>
                                <input type="text" value="${props.height}" data-prop="height">
                            </div>
                            <div class="property-item">
                                <label>背景色</label>
                                <input type="color" value="${props.bgColor}" data-prop="bgColor">
                            </div>
                            <div class="property-item">
                                <label>文字颜色</label>
                                <input type="color" value="${props.color}" data-prop="color">
                            </div>
                            <div class="property-item">
                                <label>字体大小</label>
                                <input type="text" value="${props.fontSize}" data-prop="fontSize">
                            </div>
                        `;
                        break;
                    case 'text':
                        html += `
                            <div class="property-item">
                                <label>内容</label>
                                <textarea data-prop="content">${props.content}</textarea>
                            </div>
                            <div class="property-item">
                                <label>颜色</label>
                                <input type="color" value="${props.color}" data-prop="color">
                            </div>
                            <div class="property-item">
                                <label>字体大小</label>
                                <input type="text" value="${props.fontSize}" data-prop="fontSize">
                            </div>
                            <div class="property-item">
                                <label>字体粗细</label>
                                <select data-prop="fontWeight">
                                    <option value="normal" ${props.fontWeight === 'normal' ? 'selected' : ''}>正常</option>
                                    <option value="bold" ${props.fontWeight === 'bold' ? 'selected' : ''}>加粗</option>
                                </select>
                            </div>
                        `;
                        break;
                    case 'input':
                        html += `
                            <div class="property-item">
                                <label>占位文本</label>
                                <input type="text" value="${props.placeholder}" data-prop="placeholder">
                            </div>
                            <div class="property-item">
                                <label>宽度</label>
                                <input type="text" value="${props.width}" data-prop="width">
                            </div>
                            <div class="property-item">
                                <label>高度</label>
                                <input type="text" value="${props.height}" data-prop="height">
                            </div>
                        `;
                        break;
                    case 'image':
                        html += `
                            <div class="property-item">
                                <label>图片URL</label>
                                <input type="text" value="${props.src}" data-prop="src">
                            </div>
                            <div class="property-item">
                                <label>宽度</label>
                                <input type="text" value="${props.width}" data-prop="width">
                            </div>
                            <div class="property-item">
                                <label>高度</label>
                                <input type="text" value="${props.height}" data-prop="height">
                            </div>
                        `;
                        break;
                    case 'container':
                        html += `
                            <div class="property-item">
                                <label>宽度</label>
                                <input type="text" value="${props.width}" data-prop="width">
                            </div>
                            <div class="property-item">
                                <label>高度</label>
                                <input type="text" value="${props.height}" data-prop="height">
                            </div>
                            <div class="property-item">
                                <label>背景色</label>
                                <input type="color" value="${props.bgColor}" data-prop="bgColor">
                            </div>
                        `;
                        break;
                }
                
                html += `</div>
                <button id="update-props" data-id="${id}">更新属性</button>`;
                
                propertiesContent.innerHTML = html;
                
                // 为更新按钮添加事件
                document.getElementById('update-props').addEventListener('click', function() {
                    updateComponentProperties(this.getAttribute('data-id'), type);
                });
            }
            
            // 更新组件属性
            function updateComponentProperties(id, type) {
                const component = document.getElementById(id);
                if (!component) return;
                
                const inputs = propertiesContent.querySelectorAll('[data-prop]');
                const props = {};
                
                inputs.forEach(input => {
                    const propName = input.getAttribute('data-prop');
                    props[propName] = input.value;
                });
                
                // 更新组件数据
                component.dataset.props = JSON.stringify(props);
                
                // 根据类型更新组件显示
                switch(type) {
                    case 'button':
                        component.innerHTML = `<button style="width:${props.width};height:${props.height};background-color:${props.bgColor};color:${props.color};font-size:${props.fontSize}">${props.text}</button>`;
                        break;
                    case 'text':
                        component.innerHTML = `<div style="color:${props.color};font-size:${props.fontSize};font-weight:${props.fontWeight}">${props.content}</div>`;
                        break;
                    case 'input':
                        component.innerHTML = `<input type="text" placeholder="${props.placeholder}" style="width:${props.width};height:${props.height}">`;
                        break;
                    case 'image':
                        component.innerHTML = `<img src="${props.src}" style="width:${props.width};height:${props.height}" alt="图片">`;
                        break;
                    case 'container':
                        component.style.width = props.width;
                        component.style.height = props.height;
                        component.style.backgroundColor = props.bgColor;
                        break;
                }
                
                // 重新添加调整大小手柄
                const resizeHandle = document.createElement('div');
                resizeHandle.className = 'resize-handle';
                component.appendChild(resizeHandle);
            }
            
            // 点击画布空白处取消选择
            canvasContainer.addEventListener('mousedown', function(e) {
                if (e.target === this && selectedComponent) {
                    selectedComponent.classList.remove('selected');
                    selectedComponent = null;
                    propertiesContent.innerHTML = '<p>选择一个组件来编辑其属性</p>';
                }
            });
            
            // 预览按钮
            document.getElementById('preview-btn').addEventListener('click', function() {
                alert('预览功能将在新窗口打开当前设计的页面');
                // 实际实现中可以打开一个新窗口或切换到预览模式
            });
            
            // 保存按钮
            document.getElementById('save-btn').addEventListener('click', function() {
                alert('设计已保存');
                // 实际实现中可以保存到本地或服务器
            });
            
            // 清空按钮
            document.getElementById('clear-btn').addEventListener('click', function() {
                if (confirm('确定要清空画布吗?')) {
                    canvasContainer.innerHTML = '';
                    propertiesContent.innerHTML = '<p>选择一个组件来编辑其属性</p>';
                    selectedComponent = null;
                }
            });
        });
    </script>
</body>
</html>

功能说明

这个低代码前端编辑器包含以下功能:

  1. 左侧组件面板

    • 提供可拖拽的基础组件(按钮、文本、输入框、图片、容器)
  2. 中间画布区域

    • 接收拖放的组件
    • 支持组件的选择、移动和调整大小
    • 显示当前设计的布局
  3. 右侧属性面板

    • 显示选中组件的可编辑属性
    • 支持实时更新组件样式和内容
  4. 基本交互功能

    • 拖拽添加组件
    • 组件选择和属性编辑
    • 组件移动和调整大小
    • 预览、保存和清空功能

扩展建议

要使这个编辑器更实用,你可以考虑添加以下功能:

  1. 撤销/重做功能
  2. 组件层级管理(置顶、置底)
  3. 组件复制/粘贴
  4. 响应式布局设置
  5. 导出为HTML/CSS代码
  6. 保存到本地存储或服务器
  7. 更多组件类型(表格、列表、导航栏等)
  8. 组件对齐辅助线
  9. 网格布局支持

这个实现是一个基础版本,你可以根据需要进行扩展和定制。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐