java动态生成、修改element-ui主题

193 阅读2分钟

element主题存放目录

image.png

依赖包

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>
<dependency>
    <groupId>io.bit3</groupId>
    <artifactId>jsass</artifactId>
    <version>5.7.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.12.5</version>
</dependency>

代码实现

  1. ElementCssUtils
import cn.hutool.core.io.FileUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.bit3.jsass.Compiler;
import io.bit3.jsass.Options;
import io.bit3.jsass.Output;
import io.bit3.jsass.OutputStyle;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

/**
 * element-ui 主题css生成工具
 * @author SKonst
 */
public class ElementCssUtils {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private static final String[] FILE_PREFIX = new String[] {"", "_"};

    private static final String[] FILE_SUFFIX = new String[] {".scss", ".css"};

    private ElementCssUtils() {}

    /**
     * 创建scss格式文件
     * @param properties 配置
     * @param jsonScssContent 格式 [{"key": "scss变量","value": "scss变量值}]
     * @return File
     * @throws IOException io
     */
    public static File createElementThemeScssFile(ElementThemeProperties properties, String jsonScssContent) throws IOException {
        return createElementThemeScssFile(properties, MAPPER.readTree(jsonScssContent));
    }

    public static File createElementThemeScssFile(ElementThemeProperties properties, JsonNode root) {
        File compilerScssFile = FileUtil.createTempFile("element-", properties.getImportSuffix(),
                FileUtil.file(FileUtil.getAbsolutePath(properties.getCompiledPath()) + File.separator + "tmp"), false);
        for (JsonNode node : root) {
            String content = node.get("key").asText() + " : " + node.get("value").asText() + ";\n";
            FileUtil.appendUtf8String(content, compilerScssFile);
        }
        return compilerScssFile;
    }

    @SneakyThrows
    public static File createElementThemeCssFile(ElementThemeProperties properties, File compilerScssFile) {
        Compiler compiler = new Compiler();
        Options options = new Options();
        options.setOutputStyle(OutputStyle.COMPRESSED);
        options.getImporters().add(new ElementThemeImporter(compilerScssFile.toURI(), properties));

        URI importScssUri = Paths.get(properties.getAbsolutePath()).resolve(properties.getEntranceScss()).toUri();
        String importScssContent = IOUtils.toString(importScssUri, Charset.defaultCharset());
        FileUtil.appendUtf8String(importScssContent, compilerScssFile);

        File compiledScssFile = new File(properties.getCompiledPath(),FileUtil.getPrefix(compilerScssFile) + properties.getExportSuffix());
        Output output = compiler.compileFile(compilerScssFile.toURI(), null, options);
        FileUtil.writeBytes(output.getCss().getBytes(StandardCharsets.UTF_8),compiledScssFile);

        if (properties.isTmpDel()) {
            Files.delete(compilerScssFile.toPath());
        }
        return compiledScssFile;
    }

    public static Path importScssPath(ElementThemeProperties properties, String url, URI compilerScssUri, URI previousUri) {
        if (compilerScssUri.normalize().getPath().equals(previousUri.normalize().getPath())) {
            Path absolute = Paths.get(properties.getAbsolutePath()).normalize();
            if (url.endsWith(FILE_SUFFIX[0]) || url.endsWith(FILE_SUFFIX[1])) {
                return absolute.resolve(url).normalize();
            }
            return importScssPath(url, absolute);
        }
        Path previousPath = Paths.get(previousUri.getPath()).normalize();
        return importScssPath(url, previousPath.getParent().normalize());
    }

    public static Path importScssPath(String url, Path previousParentPath) {
        Path scssPath = null;
        for (String prefix : FILE_PREFIX) {
            for (String suffix : FILE_SUFFIX) {
                if (url.endsWith(FILE_SUFFIX[0]) || url.endsWith(FILE_SUFFIX[1])) {
                    scssPath = previousParentPath.resolve(url).normalize();
                } else {
                    scssPath = previousParentPath.resolve(url + suffix).normalize();
                }
                Path targetParent = scssPath.getParent();
                String scssFileName = scssPath.getFileName().toString();
                scssPath = targetParent.resolve(prefix + scssFileName).normalize();
                if (Files.exists(scssPath)) {
                    break;
                }
            }
            if (Files.exists(scssPath)) {
                break;
            }
        }
        return scssPath;
    }

    public static URL resolveResource(Path target) throws MalformedURLException {
        return target.toUri().toURL();
    }

    public static void main(String[] args) throws IOException {
        ElementThemeProperties properties = new ElementThemeProperties();
        // element主题所在目录
        properties.setAbsolutePath("/Users/skonst/element");
        // 编译后的主题存放目录
        properties.setCompiledPath("/Users/skonst/complie-element-theme");
        // element主题入口scss文件
        properties.setEntranceScss("index.scss");
        // 导入的文件的后缀名
        properties.setImportSuffix(".scss");
        // 编译(导出)后文件的后缀名
        properties.setExportSuffix(".css");

        String jsonScssContent = "[\n" +
                "    {\n" +
                "        "key": "$--fade-linear-transition",\n" +
                "        "value": "opacity 500ms linear !default"\n" +
                "    },\n" +
                "    {\n" +
                "        "key": "$--md-fade-transition",\n" +
                "        "value": "transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default"\n" +
                "    }\n" +
                "]\n";
        // 遍历json对象并写入scss文件
        final File compilerScssFile = ElementCssUtils.createElementThemeScssFile(properties, jsonScssContent);
        final File file = ElementCssUtils.createElementThemeCssFile(properties, compilerScssFile);
        System.out.println(IOUtils.toString(file.toURI(), Charset.defaultCharset()));
    }

}
  1. ElementThemeImporter
import io.bit3.jsass.importer.Import;
import io.bit3.jsass.importer.Importer;
import lombok.SneakyThrows;
import org.apache.commons.io.IOUtils;

import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;

public class ElementThemeImporter implements Importer {

    private URI importScssUri;

    private ElementThemeProperties properties;

    public ElementThemeImporter(URI importScssUri, ElementThemeProperties properties) {
        this.importScssUri = importScssUri;
        this.properties = properties;
    }

    @SneakyThrows
    @Override
    public Collection<Import> apply(String url, Import previous) {
        URL resource = ElementCssUtils.resolveResource(ElementCssUtils.importScssPath(properties, url, importScssUri.normalize(), previous.getAbsoluteUri().normalize()));
        return Collections.singleton(new Import(resource.toURI(), resource.toURI(), IOUtils.toString(resource, StandardCharsets.UTF_8)));
    }
}
  1. ElementThemeProperties
import lombok.Data;

@Data
public class ElementThemeProperties {

    /**
     * element主题所在绝对路径
     */
    private String absolutePath;

    /**
     * scss文件编译后的路径,css输出路径
     */
    private String compiledPath;

    /**
     * scss入口文件
     */
    private String entranceScss;

    /**
     * scss文件中通过@import导入的文件后缀
     */
    private String importSuffix;

    /**
     * scss文件编译后的文件后缀
     */
    private String exportSuffix;

    /**
     * 创建的scss文件编译后是否删除
     */
    private boolean isTmpDel = true;

}