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