让Erupt框架支持.vue文件做自定义页面模版

Erupt是什么?

Erupt 是一个低代码 全栈类 框架,它使用 Java 注解 动态生成页面以及增、删、改、查、权限控制等后台功能。

零前端代码、零 CURD、自动建表,仅需 一个类文件 + 简洁的注解配置,快速开发企业级 Admin 管理后台。

提供企业级中后台管理系统的全栈解决方案,大幅压缩研发周期,专注核心业务。

官网:https://www.erupt.xyz

官方文档:https://www.yuque.com/erupts/erupt

在Erupt框架中如果需要自定义页面时,我们就需要自己写一下html的页面内容和逻辑代码,习惯了写vue的小伙伴们在写Html的页面是不是感觉有点烫手了,那下面我们就来让Erupt支持使用.vue的文件方式自定义页面内容以及业务逻辑,项目中用的 vue3-sfc-loader 这个js依赖实现vue模版的加载渲染的。

一、搭建初始化Erupt的基础项目

我们直接参考Erupt的官方文档创建一个简单的SpringBoot项目,我这里是使用了Gradle的方法去管理依赖的,和使用Maven管理是一样的,贴上我的项目整体结构如下:

项目使用到的依赖:

二、下载需要的js依赖

我在项目中没有使用Erupt中提供个UI组件,我是自己去下载的比较新的ui组件,因为我觉得Erupt提供的UI组件的版本有点低,所以我就自己去下载了一些新版的ui组件及vue3

  • Vue3    v3.5.8    文档:https://cn.vuejs.org
  • Element-plus  v2.8.3    文档:https://element-plus.org/zh-CN
  • Less   v4   文档:https://less.bootcss.com
  • Unocss  v0.62.4   文档:https://unocss.jiangruyi.com
  • Vue3-sfc-loader   v0.9.5  文档:https://gitcode.com/gh_mirrors/vu/vue3-sfc-loader

以上就是我项目中使用到的js依赖了,我这边使用的是vue3的所以对应的vue-sfc-loader 就是vue3-sfc-loader ,如果你用的是vue2 就选择vue2-sfc-loader。

ui组件我这边选的是element-plus,当然也可以使用其他的UI组件,使用的方式都是差不多的。

三、创建自定义页面

因为我这边使用的模版解析的引擎是 freemarker, 所以我的页面模版的文件是以 .ftl 结尾的,它就是一个freemarker的模版文件。模版的文件需要放在项目的 resource/tpl 文件夹下,这个是Erupt 默认要求这么放的,不然做映射是访问不到的。

1. 常规写法

demo.ftl 是常规的写法,没有使用vue3-sfc-loader,具体内容如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport"/>
    <title>demo</title>
    <link href="${base}/components/element-plus/css/index.css" rel="stylesheet">
    <link href="${base}/css/loading.css" rel="stylesheet">
</head>
<body>
<!-- loading  start -->
<div id="loader">
    <div class="rotate-loading"></div>
    <b> 页面数据加载中 。。。</b>
</div>
<!-- loading end -->
<div id="app" un-cloak>
    <div class="p3 text-2xl text-center">{{message}} &nbsp;&nbsp;---->>&nbsp;&nbsp; {{count}}</div>
    <div class="p3">
        <el-card>
            <template #header>
                element-plus 展示
            </template>
            <div class="mb-4">
                <el-button>Default</el-button>
                <el-button type="primary">Primary</el-button>
                <el-button type="success">Success</el-button>
                <el-button type="info">Info</el-button>
                <el-button type="warning">Warning</el-button>
                <el-button type="danger">Danger</el-button>
            </div>

            <div class="p3">
                <el-button circle>
                    <el-icon>
                        <Search/>
                    </el-icon>
                </el-button>
                <el-button type="primary" circle>
                    <el-icon>
                        <Edit/>
                    </el-icon>
                </el-button>
                <el-button type="success" circle>
                    <el-icon>
                        <Check/>
                    </el-icon>
                </el-button>
                <el-button type="info" circle>
                    <el-icon>
                        <Message/>
                    </el-icon>
                </el-button>
                <el-button type="warning" circle>
                    <el-icon>
                        <Star/>
                    </el-icon>
                </el-button>
                <el-button type="danger">
                    <el-icon class="mr1">
                        <Delete/>
                    </el-icon>
                    删除
                </el-button>
            </div>
        </el-card>
    </div>
</div>
</body>

<#-- freemarker 服务端变量获取 -->
<script type="application/javascript">
    var base = "${base}";
    var user = "${user}";
    console.log(base);
    console.log(user);

    function onloadRun() {
        // loading 隐藏
        window.onload = function() {
            // 页面加载后执行的代码
            let elementId = document.getElementById("loader");
            if (elementId) {
                elementId.style.display = "none";
            }
        };
    }
</script>

<#-- unocss 原子样式 https://unocss.jiangruyi.com -->
<script src="${base}/components/unocss/runtime.js"></script>

<#-- vue3 https://cn.vuejs.org -->
<script src="${base}/components/vue3/index.js"></script>

<#-- element-plus https://element-plus.org/zh-CN -->
<script src="${base}/components/element-plus/index.js"></script>

<#-- element-plus 图标 -->
<script src="${base}/components/element-plus/icons.js"></script>

<#-- element-plus 国际化修改为中文 -->
<script src="${base}/components/element-plus/zh_cn.js"></script>

<#-- 使用vue3 模板语法加载页面样例 -->
<script type="module">
    const {createApp, ref, reactive} = Vue;
    onloadRun();
    const app = createApp({
        setup() {
            const count = ref(1);
            const message = reactive("测试");
            return {
                message,
                count
            };
        }
    })
    // 引入ElementPlus组件
    app.use(ElementPlus, {
        locale: ElementPlusLocaleZhCn
    });
    // 引入ElementPlus图标
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
        app.component(key, component);
    }
    app.mount("#app")
</script>
</html>

2. vue模版加载的写法 

vue3-sfc-loader-demo.ftl 是使用了vue3-sfc-loader去加载一个.vue模板文件的写法,具体的使用方法在官方文档里面都是有的,有兴趣的可以去看看,官方有很多的例子可以参考的很详细,下面是我自己写的,页面的具体内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport"/>
    <title>vue3-sfc-loader-demo</title>
    <link href="${base}/components/element-plus/css/index.css" rel="stylesheet">
    <link href="${base}/css/loading.css" rel="stylesheet">
</head>
<body>
<!-- loading  start -->
<div id="loader">
    <div class="rotate-loading"></div>
    <b> 页面数据加载中 。。。</b>
</div>
<!-- loading end -->

<div id="app" un-cloak>
    <vue-component></vue-component>
</div>
</body>

<#-- freemarker 服务端变量获取 -->
<script type="application/javascript">
    var base = "${base}";
    var user = "${user}";
    console.log(base);
    console.log(user);

    function onloadRun() {
        // loading 隐藏
        window.onload = function () {
            // 页面加载后执行的代码
            let elementId = document.getElementById("loader");
            if (elementId) {
                elementId.style.display = "none";
            }
        };
    }
</script>

<#-- unocss 原子样式 https://unocss.jiangruyi.com -->
<script src="${base}/components/unocss/runtime.js"></script>

<#-- vue3 https://cn.vuejs.org -->
<script src="${base}/components/vue3/index.js"></script>

<#-- 加载vue3-sfc-loader https://gitcode.com/gh_mirrors/vu/vue3-sfc-loader -->
<script src="${base}/components/vue3-sfc-loader/index.js"></script>

<#-- less 依赖 https://less.bootcss.com -->
<script src="${base}/components/less/index-v4.js"></script>

<#-- element-plus https://element-plus.org/zh-CN -->
<script src="${base}/components/element-plus/index.js"></script>

<#-- element-plus 图标 -->
<script src="${base}/components/element-plus/icons.js"></script>

<#-- element-plus 国际化修改为中文 -->
<script src="${base}/components/element-plus/zh_cn.js"></script>

<#-- 使用 vue3-sfc-loader 加载页面样例 -->
<script type="module">
    const {createApp, defineAsyncComponent} = Vue;
    const {loadModule} = window['vue3-sfc-loader'];

    onloadRun();

    // 必要的参数:
    // vue 用来加载vue的模块,在vue模版中使用如: import { computed, onDeactivated, onMounted, ref } from "vue";
    // element-plus 用来加载element-plus的模块  在vue模版中使用如:import { button } from "element-plus";
    // 自定义的:
    // less 用来加载less的样式解析模块(模版使用了less就需要给定此参数)在vue模版中使用如:<style scoped lang="less"></style>
    const options = {
        moduleCache: {
            vue: Vue,
            "element-plus": ElementPlus,
            less: less
        },
        async getFile(url) {
            return await fetch(url).then(res => {
                if (!res.ok) throw Object.assign(new Error(res.statusText + " " + url), {res});
                return {
                    getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text()
                };
            });
        },
        addStyle(textContent) {
            const style = Object.assign(document.createElement("style"), {textContent});
            const ref = document.head.getElementsByTagName("style")[0] || null;
            document.head.insertBefore(style, ref);
        }
    };

    const tmpPath = top.location.pathname + "templates/myComponent.vue"

    const app = createApp({
        components: {
            'my-component': defineAsyncComponent(() => loadModule(tmpPath, options))
        },
        template: '<my-component></my-component>'
    });

    // 引入ElementPlus组件
    app.use(ElementPlus, {
        locale: ElementPlusLocaleZhCn
    });
    // 引入ElementPlus图标
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
        app.component(key, component);
    }
    app.mount("#app")
</script>
</html>

四、映射页面地址

@TplAction(value = "demo", path = "/tpl/demo/demo.ftl")

// value 是访问的值,可以在菜单的配置的值
// path 是页面的路径

五、创建访问菜单

1. 普通自定义页面

2. vue模版自定义页面

六、访问页面

七、扩展点

使用一个freemarker模版文件就可以加载所有指定名字的vue模版文件了,具体的操作案例如下:

1. 创建一个freemarker模版文件

内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <meta content="width=device-width, initial-scale=1.0, user-scalable=no" name="viewport"/>
    <title>公共页面模板</title>
    <link href="${base}/components/element-plus/css/index.css" rel="stylesheet">
    <link href="${base}/css/loading.css" rel="stylesheet">
</head>
<body>
<!-- loading  start -->
<div id="loader">
    <div class="rotate-loading"></div>
    <b> 页面数据加载中 。。。</b>
</div>
<!-- loading end -->

<div id="app" un-cloak>
    <vue-component></vue-component>
</div>
</body>

<#-- freemarker 服务端变量获取 -->
<script type="application/javascript">
    // 获取指定的vue模板名称
    var vue_tmp_name = "${vue_tmp_name}";

    function onloadRun() {
        // loading 隐藏
        window.onload = function () {
            // 页面加载后执行的代码
            let elementId = document.getElementById("loader");
            if (elementId) {
                elementId.style.display = "none";
            }
        };
    }
</script>

<#-- unocss 原子样式 https://unocss.jiangruyi.com -->
<script src="${base}/components/unocss/runtime.js"></script>

<#-- vue3 https://cn.vuejs.org -->
<script src="${base}/components/vue3/index.js"></script>

<#-- 加载vue3-sfc-loader https://gitcode.com/gh_mirrors/vu/vue3-sfc-loader -->
<script src="${base}/components/vue3-sfc-loader/index.js"></script>

<#-- less 依赖 https://less.bootcss.com -->
<script src="${base}/components/less/index-v4.js"></script>

<#-- element-plus https://element-plus.org/zh-CN -->
<script src="${base}/components/element-plus/index.js"></script>

<#-- element-plus 图标 -->
<script src="${base}/components/element-plus/icons.js"></script>

<#-- element-plus 国际化修改为中文 -->
<script src="${base}/components/element-plus/zh_cn.js"></script>

<#-- 使用 vue3-sfc-loader 加载页面样例 -->
<script type="module">
    const {createApp, defineAsyncComponent} = Vue;
    const {loadModule} = window['vue3-sfc-loader'];

    onloadRun();

    // 必要的参数:
    // vue 用来加载vue的模块,在vue模版中使用如: import { computed, onDeactivated, onMounted, ref } from "vue";
    // element-plus 用来加载element-plus的模块  在vue模版中使用如:import { button } from "element-plus";
    // 自定义的:
    // less 用来加载less的样式解析模块(模版使用了less就需要给定此参数)在vue模版中使用如:<style scoped lang="less"></style>
    const options = {
        moduleCache: {
            vue: Vue,
            "element-plus": ElementPlus,
            less: less
        },
        async getFile(url) {
            return await fetch(url).then(res => {
                if (!res.ok) throw Object.assign(new Error(res.statusText + " " + url), {res});
                return {
                    getContentData: asBinary => asBinary ? res.arrayBuffer() : res.text()
                };
            });
        },
        addStyle(textContent) {
            const style = Object.assign(document.createElement("style"), {textContent});
            const ref = document.head.getElementsByTagName("style")[0] || null;
            document.head.insertBefore(style, ref);
        }
    };

    const tmpPath = top.location.pathname + "templates/" + vue_tmp_name + ".vue"

    const app = createApp({
        components: {
            'my-component': defineAsyncComponent(() => loadModule(tmpPath, options))
        },
        template: '<my-component></my-component>'
    });

    // 引入ElementPlus组件
    app.use(ElementPlus, {
        locale: ElementPlusLocaleZhCn
    });
    // 引入ElementPlus图标
    for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
        app.component(key, component);
    }
    app.mount("#app")
</script>
</html>

2. 创建两个vue模版文件用了测试

路径是按照上面的 tmp.ftl 里面的第104行定义的,如果你不想放这里相放在其他的地方,就可以修改这个路径

3. 在Erupt实体模型中使用

在这个模型中定义了两个按钮,点击按钮就会访问特定的页面文件,让后通过配置的 params 这个名字,页面就能拿到‘vue_tmp_name’ 的名字去加载指定的vue文件了

4. 访问页面

以上就是整个的使用过程,不明白的可以去拉取代码自己运行即可。

八、 源代码

代码路径:https://gitee.com/shareloke/erupt-vue3-sfc-loader-demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/908703.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Echarts 图表根据屏幕大小自适应图表大小/标签文字大小

自适应图表大小 echarts多个图表大小随屏幕的大小改变自适应&#xff0c;Echarts 多图表自适应窗口大小&#xff0c;echarts随页面大小变化而变化&#xff1b; 但 Echarts 同一页面存在多个图表的时候&#xff0c;只有一个生效 只有一个图表的时候 直接用 window.onresize …

基于 Transformer 的语言模型

基于 Transformer 的语言模型 Transformer 是一类基于注意力机制&#xff08;Attention&#xff09;的模块化构建的神经网络结构。给定一个序列&#xff0c;Transformer 将一定数量的历史状态和当前状态同时输入&#xff0c;然后进行加权相加。对历史状态和当前状态进行“通盘…

Docker:容器编排 Docker Compose

Docker&#xff1a;容器编排 Docker Compose docker-composedocker-compose.ymlservicesimagecommandenvironmentnetworksvolumesportshealthcheckdepends_on 命令docker compose updocker compose down其它 docker-compose 多数情况下&#xff0c;一个服务需要依赖多个服务&a…

力扣633.平方数之和 c++

给定一个非负整数 c &#xff0c;你要判断是否存在两个整数 a 和 b&#xff0c;使得 a2 b2 c 。 示例 1&#xff1a; 输入&#xff1a;c 5 输出&#xff1a;true 解释&#xff1a;1 * 1 2 * 2 5示例 2&#xff1a; 输入&#xff1a;c 3 输出&#xff1a;false提示&…

【ESP32】ESP-IDF开发 | I2C从机接收i2c_slave_receive函数的BUG导致程序崩溃解决(idf-v5.3.1版本)

1. 问题 在调试I2C外设的demo时&#xff0c;按照官方文档的描述调用相关API&#xff0c;烧录程序后发现程序会不断崩溃&#xff0c;系统log如下。 初步分析log&#xff0c;原因是访问到了不存在的地址。一开始我以为是自己的代码问题&#xff0c;反反复复改了几次都会出现同样的…

链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)

AB链表公共元素生成链表C 设A和B是两个单链表(带头节点)&#xff0c;其中元素递增有序。设计一个算法从A和B中的公共元素产生单链表C&#xff0c;要求不破坏A、B的节点 算法思想 表A&#xff0c;B都有序&#xff0c;可从第一个元素起依次比较A、B两表的元素&#xff0c;若元…

蓝牙BLE开发——红米手机无法搜索蓝牙设备?

解决 红米手机&#xff0c;无法搜索附近蓝牙设备 具体型号当时忘记查看了&#xff0c;如果你遇到有以下选项&#xff0c;记得打开~ 设置权限

2-143 基于matlab-GUI的脉冲响应不变法实现音频滤波功能

基于matlab-GUI的脉冲响应不变法实现音频滤波功能&#xff0c;输入加噪信号&#xff0c;通过巴特沃斯模拟滤波器脉冲响应不变法进行降噪。效果较好。程序已调通&#xff0c;可直接运行。 下载源程序请点链接&#xff1a;2-143 基于matlab-GUI的脉冲响应不变法实现音频滤波功能…

智慧生活新标准:解锁耐能科技的潜能

『从马路上&#xff0c;看到旁边摄影机下方的牌子写着科技执法』 『开车进入停车场时&#xff0c;车牌辨识成功开启闸门』 『回到家门口前&#xff0c;进行脸部辨识解锁开门』 『手机APP弹起提醒&#xff0c;出现宝宝的画面表示正在哭泣』 上述的情景&#xff0c;你我是否很熟悉…

[VUE]框架网页开发1 本地开发环境安装

前言 其实你不要看我的文章比较长&#xff0c;但是他就是很长&#xff01;步骤其实很简单&#xff0c;主要是为新手加了很多解释&#xff01; 步骤一&#xff1a;下载并安装 Node.js 访问 Node.js 官网&#xff1a; Node.js — Download Node.js 下载 Windows 64 位版本&…

C++线程异步

本文内容来自&#xff1a; 智谱清言 《深入应用C11 代码优化与工程级应用》 std::future std::future作为异步结果的传输通道&#xff0c;可以很方便地获取线程函数的返回值。 std::future_status Ready (std::future_status::ready): 当与 std::future 对象关联的异步操作…

【Python】【数据可视化】【商务智能方法与应用】课程 作业一 飞桨AI Studio

作业说明 程序运行和题目图形相同可得90分&#xff0c;图形显示有所变化&#xff0c;美观清晰可适当加分。 import matplotlib.pyplot as plt import numpy as npx np.linspace(0, 1, 100) y1 x**2 y2 x**4plt.figure(figsize(8, 6))# yx^2 plt.plot(x, y1, -., labelyx^2,…

后端eclipse——文字样式:UEditor富文本编辑器引入

目录 1.富文本编辑器的优点 2.文件的准备 3.文件的导入 导入到项目&#xff1a; 导入到html文件&#xff1a; ​编辑 4.富文本编辑器的使用 1.富文本编辑器的优点 我们从前端写入数据库时&#xff0c;文字的样式具有局限性&#xff0c;不能存在换行&#xff0c;更改字体…

基于springboot+小程序的汽车销售管理系统(汽车4)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 ​ 1、管理员实现了首页、个人中心、管理员管理、基础数据管理、论坛管理、公告信息管理、汽车管理、用户管理、轮播图信息等。 ​ 2、用户实现了注册、登录、首页、汽车类型、论坛、购物…

江协科技STM32学习- P29 实验- 串口收发HEX数据包/文本数据包

&#x1f680;write in front&#x1f680; &#x1f50e;大家好&#xff0c;我是黄桃罐头&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流 &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​…

Quartz的使用

1.准备工作 建立Maven工程 2.引入Quartz的jar包 <dependencies><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.0</version></dependency></dependencies>3…

黑马官网最新2024前端就业课V8.5笔记---CSS篇(2)

盒子模型 画盒子 目标:使用合适的选择器画盒子 新属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vi…

单片机串口接收状态机STM32

单片机串口接收状态机stm32 前言 项目的芯片stm32转国产&#xff0c;国产芯片的串口DMA接收功能测试不通过&#xff0c;所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣&#xff0c;不过我的芯片已经主频跑…

Android Studio 安装过程

以前用 Eclipse 开发&#xff0c;最近尝试 Android Studio 开发&#xff0c;发现 Android Studio 比 Eclipse 速度快多了&#xff0c;下面是安装 Android Studio 过程日志。 Gradle 下载地址&#xff1a;https://services.gradle.org/distributions/ https://developer.andro…

github.io出现的问题及解决方案

1. 你的连接不是专用连接 放假回家后打开自己的博客&#xff0c;发现无法打开博客&#xff0c;一开始以为是调样式时不小心搞坏了&#xff0c;打开别人的githunb.io博客发现都会出问题&#xff0c;并且用手机不连接wifi可以正常打开 解决办法&#xff1a; 方法一&#xff1a; …