package com.java110.web.core; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.thymeleaf.context.ITemplateContext; import org.thymeleaf.exceptions.TemplateProcessingException; import org.thymeleaf.model.IModel; import org.thymeleaf.model.IModelFactory; import org.thymeleaf.model.IProcessableElementTag; import org.thymeleaf.processor.element.AbstractElementTagProcessor; import org.thymeleaf.processor.element.IElementTagStructureHandler; import org.thymeleaf.templatemode.TemplateMode; import java.util.Date; public class VcCreateProcessor extends AbstractElementTagProcessor { private static Logger logger = LoggerFactory.getLogger(VcCreateProcessor.class); private static final String DIV_PROPERTY_COMPONENT = "data-component"; private static final String TAG_NAME = "create"; private static final String NAME = "name"; private static final int PRECEDENCE = 300; public VcCreateProcessor(String dialectPrefix) { super(TemplateMode.HTML, dialectPrefix, TAG_NAME, true, null, false, PRECEDENCE); } @Override protected void doProcess(ITemplateContext context, IProcessableElementTag tag, IElementTagStructureHandler structureHandler) { //获取模板名称 String componentName = tag.getAttributeValue(NAME); logger.debug("正在解析组件{},{}", componentName, new Date().getTime()); String html = VueComponentTemplate.findTemplateByComponentCode(componentName + "." + VueComponentTemplate.COMPONENT_HTML); if (html == null) { throw new RuntimeException("在缓存中未找到组件【" + componentName + "】"); } IModelFactory modelFactory = context.getModelFactory(); IModel htmlModel = modelFactory.createModel(); Document doc = Jsoup.parseBodyFragment(html); //获取我们真实的一个元素,去除因为转换而加入的 html 和body 标签 Elements elements = doc.body().children(); //将组建名称写入组建HTML 第一个标签中 addDataComponent(elements, componentName); htmlModel.addModel(modelFactory.parse(context.getTemplateData(), doc.body().children().toString())); String css = VueComponentTemplate.findTemplateByComponentCode(componentName + "." + VueComponentTemplate.COMPONENT_CSS); if (css != null) { css = ""; htmlModel.add(modelFactory.createText(css)); } //js String js = VueComponentTemplate.findTemplateByComponentCode(componentName + "." + VueComponentTemplate.COMPONENT_JS); if (js != null) { js = dealJs(js, tag); js = dealJsAddComponentCode(js, tag); js = ""; htmlModel.add(modelFactory.createText(js)); } logger.debug("解析完成组件{},{}", componentName, new Date().getTime()); structureHandler.replaceWith(htmlModel, true); } /** * 加入组件名称到 HTML中 方便定位问题 * * @param elements 页面节点 * @param componentCode 组件编码 */ private void addDataComponent(Elements elements, String componentCode) { Element tmpElement = elements.get(0); tmpElement.attr(DIV_PROPERTY_COMPONENT, componentCode); } /** * 处理js * * @param tag 页面元素 * @param js js文件内容 * @return js 文件内容 */ private String dealJs(String js, IProcessableElementTag tag) { //在js 中检测propTypes 属性 if (!js.contains("propTypes")) { return js; } //解析propTypes信息 String tmpProTypes = js.substring(js.indexOf("propTypes")); tmpProTypes = tmpProTypes.substring(tmpProTypes.indexOf("{") + 1, tmpProTypes.indexOf("}")).trim(); if (StringUtils.isEmpty(tmpProTypes)) { return js; } tmpProTypes = tmpProTypes.contains("\r")? tmpProTypes.replace("\r", "") : tmpProTypes; String[] tmpType = tmpProTypes.contains("\n") ? tmpProTypes.split("\n") : tmpProTypes.split(","); StringBuffer propsJs = new StringBuffer("\nvar $props = {};\n"); for (String type : tmpType) { if (StringUtils.isEmpty(type) || !type.contains(":")) { continue; } String[] types = type.split(":"); String attrKey = ""; if (types[0].contains("//")) { attrKey = types[0].substring(0, types[0].indexOf("//")); } attrKey = types[0].replace(" ", "") .replace("\n", "") .replace("\r", ""); if (!tag.hasAttribute(attrKey) && !types[1].contains("=")) { String componentName = tag.getAttributeValue("name"); logger.error("组件" + componentName + "未配置组件属性 " + attrKey); throw new TemplateProcessingException("组件[" + componentName + "]未配置组件属性" + attrKey); } String vcType = tag.getAttributeValue(attrKey); if(!tag.hasAttribute(attrKey) && types[1].contains("=")) { vcType = dealJsPropTypesDefault(types[1]); }else if (types[1].contains("vc.propTypes.string")) { vcType = "'" + vcType + "'"; } propsJs.append("$props." + attrKey + "=" + vcType + ";\n"); } //将propsJs 插入到 第一个 { 之后 int position = js.indexOf("{"); if (position < 0) { String componentName = tag.getAttributeValue("name"); logger.error("组件" + componentName + "对应js 未包含 {} "); throw new TemplateProcessingException("组件" + componentName + "对应js 未包含 {} "); } js = new StringBuffer(js).insert(position + 1, propsJs).toString(); return js; } private String dealJsPropTypesDefault(String typeValue){ int startPos = typeValue.indexOf("=") + 1; int endPos = typeValue.length(); if(typeValue.contains(",")){ endPos = typeValue.indexOf(","); }else if(typeValue.contains("//")){ endPos = typeValue.indexOf("//"); } return typeValue.substring(startPos,endPos); } /** * 处理js 变量和 方法都加入 组件编码 * * @param tag 页面元素 * @param js js文件内容 * @return js 文件内容 */ private String dealJsAddComponentCode(String js, IProcessableElementTag tag) { if (!tag.hasAttribute("code")) { return js; } String code = tag.getAttributeValue("code"); return js.replace("@vc_", code); } }