200 lines
4.6 KiB
JavaScript
200 lines
4.6 KiB
JavaScript
import { parse, generate, baseParse } from '@vue/compiler-dom';
|
||
|
||
/**
|
||
* Vue模板编辑器类
|
||
* 支持节点的移动、交换、修改等操作
|
||
*/
|
||
export class VueTemplateEditor {
|
||
constructor(template) {
|
||
this.originalTemplate = template;
|
||
this.ast = parse(template);
|
||
}
|
||
|
||
/**
|
||
* 获取模板的AST结构
|
||
*/
|
||
getAST() {
|
||
return this.ast;
|
||
}
|
||
|
||
/**
|
||
* 根据选择器查找节点
|
||
* @param {string} selector - 选择器(标签名、class、id等)
|
||
* @returns {Array} 匹配的节点数组
|
||
*/
|
||
findNodes(selector) {
|
||
const results = [];
|
||
this._traverseAST(this.ast, (node) => {
|
||
if (this._matchSelector(node, selector)) {
|
||
results.push(node);
|
||
}
|
||
});
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* 移动节点到新位置
|
||
* @param {Object} node - 要移动的节点
|
||
* @param {Object} targetParent - 目标父节点
|
||
* @param {number} position - 在子节点中的位置
|
||
*/
|
||
moveNode(node, targetParent, position = -1) {
|
||
// 从原位置移除节点
|
||
this._removeNodeFromParent(node);
|
||
|
||
// 添加到新位置
|
||
if (!targetParent.children) {
|
||
targetParent.children = [];
|
||
}
|
||
|
||
if (position === -1) {
|
||
targetParent.children.push(node);
|
||
} else {
|
||
targetParent.children.splice(position, 0, node);
|
||
}
|
||
|
||
// 更新父节点引用
|
||
node.parent = targetParent;
|
||
}
|
||
|
||
/**
|
||
* 交换两个节点的位置
|
||
* @param {Object} node1 - 第一个节点
|
||
* @param {Object} node2 - 第二个节点
|
||
*/
|
||
swapNodes(node1, node2) {
|
||
if (node1.parent !== node2.parent) {
|
||
throw new Error('只能交换同一父节点下的兄弟节点');
|
||
}
|
||
|
||
const parent = node1.parent;
|
||
const index1 = parent.children.indexOf(node1);
|
||
const index2 = parent.children.indexOf(node2);
|
||
|
||
if (index1 === -1 || index2 === -1) {
|
||
throw new Error('节点不在父节点的子节点列表中');
|
||
}
|
||
|
||
// 交换位置
|
||
parent.children[index1] = node2;
|
||
parent.children[index2] = node1;
|
||
}
|
||
|
||
/**
|
||
* 修改节点属性
|
||
* @param {Object} node - 要修改的节点
|
||
* @param {Object} attributes - 新的属性对象
|
||
*/
|
||
modifyNodeAttributes(node, attributes) {
|
||
if (!node.props) {
|
||
node.props = [];
|
||
}
|
||
|
||
Object.entries(attributes).forEach(([key, value]) => {
|
||
const existingPropIndex = node.props.findIndex(prop =>
|
||
prop.name === key || (prop.arg && prop.arg.content === key)
|
||
);
|
||
|
||
if (existingPropIndex !== -1) {
|
||
// 更新现有属性
|
||
const prop = node.props[existingPropIndex];
|
||
if (prop.value) {
|
||
prop.value.content = value;
|
||
}
|
||
} else {
|
||
// 添加新属性
|
||
node.props.push({
|
||
type: 6, // 属性节点
|
||
name: key,
|
||
value: {
|
||
type: 2, // 文本节点
|
||
content: value
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 生成修改后的模板代码
|
||
* @returns {string} 新的模板代码
|
||
*/
|
||
generateTemplate() {
|
||
const result = generate(this.ast, {
|
||
mode: 'module',
|
||
source: this.originalTemplate
|
||
});
|
||
return result.code;
|
||
}
|
||
|
||
/**
|
||
* 遍历AST树
|
||
*/
|
||
_traverseAST(node, callback) {
|
||
callback(node);
|
||
|
||
if (node.children) {
|
||
node.children.forEach(child => {
|
||
if (child.type) { // 有效的AST节点
|
||
this._traverseAST(child, callback);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 匹配选择器
|
||
*/
|
||
_matchSelector(node, selector) {
|
||
if (!node.tag) return false;
|
||
|
||
// 简单选择器匹配(可扩展)
|
||
if (selector.startsWith('.')) {
|
||
// class选择器
|
||
const className = selector.slice(1);
|
||
return node.props?.some(prop =>
|
||
prop.name === 'class' && prop.value?.content.includes(className)
|
||
);
|
||
} else if (selector.startsWith('#')) {
|
||
// id选择器
|
||
const id = selector.slice(1);
|
||
return node.props?.some(prop =>
|
||
prop.name === 'id' && prop.value?.content === id
|
||
);
|
||
} else {
|
||
// 标签名选择器
|
||
return node.tag === selector;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 从父节点移除节点
|
||
*/
|
||
_removeNodeFromParent(node) {
|
||
if (node.parent && node.parent.children) {
|
||
const index = node.parent.children.indexOf(node);
|
||
if (index !== -1) {
|
||
node.parent.children.splice(index, 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 工具函数:创建新的Vue模板编辑器实例
|
||
*/
|
||
export function createTemplateEditor(template) {
|
||
return new VueTemplateEditor(template);
|
||
}
|
||
|
||
/**
|
||
* 工具函数:格式化模板代码
|
||
*/
|
||
export function formatTemplate(template) {
|
||
const ast = parse(template);
|
||
const result = generate(ast, {
|
||
mode: 'module',
|
||
source: template
|
||
});
|
||
return result.code;
|
||
} |