This commit is contained in:
2025-11-24 11:37:59 +08:00
commit a1dd449880
10 changed files with 923 additions and 0 deletions

View File

@@ -0,0 +1,200 @@
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;
}