Xây dựng trình chỉnh sửa H5 trực quan với Vue và Element UI

Dự án pl-drag-template là một trình soạn thảo H5 dạng kéo-thả, cho phép người dùng tạo trang web di động tương tác mà không cần viết mã. Ý tưởng bắt nguồn từ các nền tảng như Yiqixiu hay Baidu H5, nhưng được triển khai lại từ đầu bằng Vue.js.

GitHub: https://github.com/livelyPeng/pl-drag-template

Demo trực tuyến: Mở bằng Chrome để có trải nghiệm tốt nhất. Khi nhấn "Lưu", hệ thống xuất ra một chuỗi JSON — dữ liệu này có thể được dùng để render trên thiết bị di động hoặc nền tảng khác.

Cấu trúc dự án

src/
├── apiUrl/               // Đường dẫn API
├── assets/               // Tài nguyên tĩnh (hình ảnh, font…)
├── components/           // Component dùng chung
├── module/
│   └── editor/           // Module trình chỉnh sửa
│       ├── components/   // Component riêng của editor
│       │   └── attributeConfig/  // Panel cấu hình thuộc tính bên phải
│       ├── pluginLibrary/        // Thư viện component kéo-thả (cốt lõi)
│       ├── routers/              // Định tuyến nội bộ
│       ├── style/                // CSS riêng
│       ├── utils/                // Hàm tiện ích
│       ├── vuex/                 // State management cục bộ
│       ├── viewPage/             // Trang giao diện chính
│       └── index.js              // Export module
├── vuex/                 // Store toàn cục
├── themes/               // Style chung (LESS)
└── utils/                // Công cụ hỗ trợ toàn dự án

Công nghệ sử dụng

  • Vue.js: Framework frontend chính
  • Vuex: Quản lý trạng thái
  • Element UI: Thư viện giao diện sẵn có
  • Less: Tiền xử lý CSS
  • Lodash: Xử lý dữ liệu

Thiết lập môi trường

Dự án được khởi tạo từ vue-cli@2 và điều chỉnh cấu trúc như sau:

project-root/
├── client/          // Mã frontend (trước là src/)
├── server/          // Backend (nếu có)
├── engine-template/ // Template render H5
└── docs/            // Tài liệu

Cấu hình Webpack được tùy chỉnh để hỗ trợ LESS, tải tài nguyên tĩnh, và alias đường dẫn.

Mô hình dữ liệu cốt lõi

Dữ liệu biểu diễn trang H5 được lưu dưới dạng JSON. Mỗi thành phần (component) trong thư viện kéo-thả tuân theo cấu trúc chuẩn:

// Cấu trúc container mặc định
const plContainer = {
  elName: 'pl-container',
  title: 'Khối chứa tự do',
  icon: 'iconfont iconrongqi',
  pointList: ['b'], // Điểm kéo (bottom)
  containerStyle: { marginBottom: 10 },
  allowed: true,    // Cho phép thả component vào
  showTitle: true,
  titleStyle: { height: 50, lineHeight: 50 },
  commonStyle: {
    width: 375,
    height: 250,
    position: 'relative',
    minHeight: 50,
    backgroundColor: '#fff'
  },
  childNode: [] // Danh sách con
};

// Ví dụ component văn bản
{
  elName: 'pl-text',
  title: 'Văn bản',
  icon: 'iconfont iconwenbenyu',
  pointList: [],
  contenteditable: false,
  placeholder: 'Nhập nội dung...',
  commonStyle: {
    padding: 8,
    fontSize: 15,
    lineHeight: 17,
    textAlign: 'left',
    minWidth: 35,
    width: 160
  }
}

Các component được phân loại thành: - Component thường: Có thể kéo thả và chỉnh sửa tự do. - Module: Component phức tạp (như carousel, nav cube), được bao bởi container nhưng không cho phép kéo-thả bên trong.

Triển khai trình chỉnh sửa

Sử dụng tính năng dynamic component của Vue để render các phần tử từ JSON:

function getNodeElement(nodeData, contextType) {
  if (!nodeData || !isValidComponent(nodeData.elName)) return null;

  const props = getInitialProps(nodeData.elName);
  let element = cloneDeep(createElementConfig({ ...nodeData, props }));

  // Nếu không phải container và không ở chế độ chèn vào container → bọc trong container
  if (element.elName !== 'pl-container' && contextType !== 'insert-into-container') {
    const container = cloneDeep(createElementConfig({
      ...plContainer,
      props: getInitialProps('pl-container')
    }));

    if (element.module) {
      const customContainerOpts = element.containerOptions || defaultModuleContainer;
      Object.assign(container, customContainerOpts);
      delete element.containerOptions;
      container.childNode.push(element);
      return container;
    }

    container.childNode.push(element);
    return container;
  }

  return element;
}

Thư viện component

Các component được thiết kế để hỗ trợ cả import toàn cục và theo nhu cầu:

import plText from './text';
import plButton from './button';
import plContainer from './container';

const components = [plText, plButton, plContainer];

const install = (Vue) => {
  if (install.installed) return;
  install.installed = true;
  components.forEach(comp => Vue.component(comp.name, comp));
};

export { plText, plButton, plContainer };
export default { install };

Chạy dự án

npm run dev

Thẻ: Vue.js Element UI H5 Editor Drag and Drop Visual Builder

Đăng vào ngày 1 tháng 10 lúc 16:11