Eclipse插件开发六:使用Web前端技术开发AI助手页面
- 互联网
- 2025-08-26 04:30:01

之前的过程中,我们都不怎么熟悉Eclipse的哪些API,样式也没发怎么去修改,现在我们要修改为用html的方式来编写. 准备一个AI助手聊天页面的html.css,js代码 效果如下所示。
1.快速demo 1.1.准备前端代码确保准备的前端代码可以在浏览器正常运行,这里我们直接浏览器访问html的绝对路径 代码目录如下所示
1.1.1.chat.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>AI助手</title> <link rel="stylesheet" type="text/css" href="F:\work\com.hutao.search\src\resources\chat.css"> </head> <body> <div class="chat-container"> <div class="messages" id="message-container"></div> <div class="input-container"> <input type="text" class="user-input" id="user-input" placeholder="输入消息..."> <button class="send-button" onclick="handleUserInput()">发送</button> </div> </div> <script src="F:\work\com.hutao.search\src\resources\chat.js"></script> </body> </html> 1.1.2.chat.js const messageContainer = document.getElementById('message-container'); const userInput = document.getElementById('user-input'); function handleUserInput() { const userMessage = userInput.value.trim(); if (userMessage === '') { return; } appendMessage(userMessage, 'user-message'); // Simulate AI Assistant reply const systemReply = 'AI助手: 你好呀,勇士!'; appendMessage(systemReply, 'assistant-message'); userInput.value = ''; messageContainer.scrollTop = messageContainer.scrollHeight; } function appendMessage(message, messageType) { const messageElement = document.createElement('div'); messageElement.classList.add('message', messageType); messageElement.textContent = message; messageContainer.appendChild(messageElement); } 1.1.3.chat.css body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f2f2f2; } .chat-container { max-width: 600px; margin: 20px auto; background-color: #fff; border-radius: 5px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); } .messages { min-height: 300px; max-height: 400px; overflow-y: scroll; padding: 10px; } .message { margin-bottom: 10px; padding: 10px; border-radius: 5px; } .user-message { background-color: #DCF8C6; align-self: flex-end; } .assistant-message { background-color: #E4E4E4; } .input-container { display: flex; align-items: center; padding: 10px; background-color: #f9f9f9; } .user-input { flex: 1; height: 40px; margin-right: 10px; padding: 5px; border: 1px solid #ccc; border-radius: 5px; } .send-button { padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 5px; cursor: pointer; } 2.改造后端代码ViewPart将之前的ViewPart 进行改造,这里不在使用java来开发界面。我们用前端的html,css,js. 实现思路,就是将在嵌入浏览器组件 (Browser) 来加载 HTML 和 JavaScript 页面,从而实现你的需求。这种方式可以让你使用熟悉的前端 技术来构建界面。
package com.hutao.search.view; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; public class AiView extends ViewPart { public static final String ID = "com.hutao.search.ai.plugin.aiview"; @Override public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); // 创建 Browser 控件 Browser browser = new Browser(parent, SWT.NONE); browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // 加载 HTML 文件内容 String htmlContent = loadHtmlFile("resources/chat.html"); System.out.println(htmlContent); // 在 Browser 控件中显示 HTML 内容 browser.setText(htmlContent); } /** * @description:加载html页面 * @author:hutao * @mail:hutao1@epri.sgcc * @date:2025年2月18日16:01:01 */ private String loadHtmlFile(String resourcePath) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath); if (inputStream == null) { return "<html><body><h1>无法加载资源</h1></body></html>"; } StringBuilder content = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) { String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); return "<html><body><h1>无法加载资源</h1></body></html>"; } return content.toString(); } @Override public void setFocus() { } } 3.Demo效果展示左边为我们在正常浏览器里面运行的效果,而右边实在eclipse中运行的效果。不然看出,样式好像丢了一些。也就是将前端的代码,迁移到html中的时候,好像不是完全兼容的。
4.CSS样式丢失的问题根据上图的对比,不难发现,背景色都丢了,我们通过浏览器查看到这个元素,复制一下这些样式 我们把这部分代码复制出来看看,然后放到我们的html代码中,
<div class="chat-container"> <div class="messages" id="message-container"> <div class="message user-message">xxx</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div> <div class="message user-message">xxx</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div> <div class="message user-message">xxx</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div> <div class="message user-message">xxx</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div> </div> <div class="input-container"> <input type="text" class="user-input" id="user-input" placeholder="输入消息..."> <button class="send-button" onclick="handleUserInput()">发送</button> </div> </div>如下图所示,通过代码直接写的,样式没问题,通过handleUserInput-》appendMessage-》追加的消息,丢失了背景色样式, 而在浏览器中,我们可以看到,写死的,和通过handleUserInput-》appendMessage-》都是正常的,观察代码,也发先,代码都是一致的,目前我们则怀疑,可能在eclipse的Browser通过我们的handleUserInput-》appendMessage-》追加的消息,可能不能是我们正常显示时候的代码了。 于是我尝试这样解决问题,那我直接用style,不用css来渲染,看看如何?结果样式正常了。
function appendMessage(message, messageType) { const messageElement = document.createElement('div'); const messageElement = document.createElement('div'); // 设置背景色和字体颜色,直接通过 style 属性应用 if (messageType === 'user-message') { messageElement.style.backgroundColor = '#DCF8C6'; // 用户消息背景色 messageElement.style.alignSelf = 'flex-end'; // 用户消息右对齐 } else if (messageType === 'assistant-message') { messageElement.style.backgroundColor = '#E4E4E4'; // AI 助手背景色 } // 直接应用原本的 CSS 样式 messageElement.style.padding = '10px'; // 设置内边距 messageElement.style.marginBottom = '10px'; // 设置下边距 messageElement.style.borderRadius = '5px'; // 设置圆角 messageElement.style.fontFamily = 'Arial, sans-serif'; // 设置字体 messageElement.style.fontSize = '14px'; // 设置字体大小 messageElement.style.lineHeight = '1.5'; // 设置行高 messageElement.style.maxWidth = '100%'; // 限制宽度(可选) messageElement.style.display = 'block'; // 确保它是块级元素 messageElement.textContent = message; messageContainer.appendChild(messageElement); }现在基本可以确定,问题出在handleUserInput-》appendMessage-》css渲染出现问题了,我尝试将下面的classList.add(‘message’, messageType)拆成两行,classList.add(‘message’),classList.add(messageType),结果样式可以正常了。
function appendMessage(message, messageType) { const messageElement = document.createElement('div'); //messageElement.classList.add('message', messageType); messageElement.classList.add('message'); messageElement.classList.add(messageType); messageElement.textContent = message; messageContainer.appendChild(messageElement); }现在就有一个问题,怎么去看生成的这些元素样式,毕竟不是在浏览器里面,浏览器我们可以F12查看。 这里这特了一早上,我最后研究了下日志打印来看看为啥上面CSS样式失效。
5.日志打印问题接着上面的问题,以后出现这样的问题该怎么定位处理?毕竟这个不想浏览器一样,我们可以按F12,可以debug
通过上面我们一系列测试,我们最终锁定:通过handleUserInput-》appendMessage-》追加的消息,丢失了背景色样式,那么如果我们能通过一些有效的方法来看看,我们最终的页面html元素是否为下面这样。
<div class="messages" id="message-container"> <div class="message user-message">aa</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div> </div> 5.1.alert弹框打印信息alert是原始的js弹框,不用引入任何框架插件即可使用,后面为啥会说,为啥用原始js打印(还有插件引入的问题还没说,这里我引入css,js是使用绝对路径地址) 在合适的位置,使用alert,例如,这里我在调用appendMessage之后,去获取message-container元素,然后弹框
const messageContainer = document.getElementById('message-container'); alert(messageContainer.innerHTML);此时我们就能发现问题了,我们通过appendMessage生成的最后html和我们预期的不一样,
实际生成的没样式 <div class="message">abc</div> <div class="message">AI助手: 你好呀,勇士!</div> 预期的生成的有样式 <div class="message user-message">aa</div> <div class="message assistant-message">AI助手: 你好呀,勇士!</div>然而,当我们把appendMessage中的classList.add(),拆成两次添加以后,就正常了。
//messageElement.classList.add('message', messageType); messageElement.classList.add('message'); messageElement.classList.add(messageType);楼主查阅了很多资料,觉得下面这个理由比较靠谱。
浏览器对标准的 JavaScript API 支持较好,classList.add 方法在现代浏览器中已经是一个标准且稳定的 API。然而,Eclipse 插件的运行环境可能和浏览器有所不同,可能存在对某些 JavaScript 特性支持不完全或者存在兼容性问题。
所以在调用一些前端的API,如果达不到我们的期望的时候,不妨用打印的办法看看。
5.2.使用console.log进行日志输出如下图所示,只是使用console.log就没那么简单了,你会发现无论怎么打印都不会输出到控制台,毕竟以前我们打印输出,是输出到F12,但是这里不好意思,这里是IDE,不是浏览器,没有F12 下面来说,怎么让console.log进行日志输出 BrowserFunction 是 Eclipse SWT(Standard Widget Toolkit)库中的一个类,用于在 Java 代码和嵌入在 SWT Browser 组件中的 JavaScript 代码之间建立桥梁,允许 JavaScript 代码调用 Java 方法。
public class CustomFunction extends BrowserFunction { public CustomFunction(Browser browser, String name) { super(browser, name); } @Override public Object function(Object[] arguments) { for (Object arg : arguments) { if (arg != null) { // 打印到Java控制台 System.out.println(arg.toString()); } } return null; } }接着在我们的ViewPart中中的创建part的方法中,创建CustomFunction。
public class AiView extends ViewPart { public static final String ID = "com.hutao.search.ai.plugin.aiview"; @Override public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); // 创建 Browser 控件 Browser browser = new Browser(parent, SWT.NONE); browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // 加载 HTML 文件内容 String htmlContent = loadHtmlFile("resources/chat.html"); System.out.println(htmlContent); // 在 Browser 控件中显示 HTML 内容 //browser.setText(htmlContent); browser.setUrl("F:\\work\\com.hutao.search\\src\\resources\\chat.html"); new CustomFunction(browser, "logToJava"); } }然后在我们的js文件(需要调用console.log 之前,重写console.log),最好js的第一行代码
console.log = function() { const messages = Array.prototype.slice.call(arguments); // 将所有参数连接成一个字符串并调用Java方法 logToJava(messages.join(' ')); }; 6.CSS,JS资源引用问题上面的代码,我用的是绝对路径地址,但是这样明显不对的,这样就没办法迁移了,因此要换成相对地址
<script src="F:\work\com.hutao.search\src\resources\chat.js"></script> <link rel="stylesheet" type="text/css" href="F:\work\com.hutao.search\src\resources\chat.css">chat.html和chat.css等都在一个文件夹,因此我们用相对路径
<script src="chat.js"></script> <link rel="stylesheet" type="text/css" href="chat.css">修改完以后再看我们的浏览器的,没有受到影响。 但是在看我们的eclipse里面的。样式不仅丢了,点击发送也没任何反应。之前是CSS部分丢了,现在来看是CSS和JS,都全丢了
6.1.问题分析在 Eclipse SWT 中的 Browser 控件加载 HTML 文件时,相对路径的资源(如 CSS 文件和 JS 文件)通常会出现问题。这是因为 Browser 控件使用的本地 Web 渲染引擎和我们普通的浏览器渲染不是一样的。因此,不能用浏览器去引用CSS,JS的思路去。 我们先不防获取一下这里的CSS,JS,HTML的路径看一下
URL jspath = getClass().getClassLoader().getResource("resources/chat.js"); URL csspath = getClass().getClassLoader().getResource("resources/chat.css"); URL htmlpath = getClass().getClassLoader().getResource("resources/chat.html"); System.out.println(jspath.toString()); System.out.println(csspath.toString()); System.out.println(htmlpath.toString());如下图所示,如果你没接触到OSGI这类插件式开发的框架,对于下面这个bundleresource的地址,一定是很蒙蔽的。
格式特点:以 bundleresource:// 开头,后面紧跟一串数字(如 729.fwk1177963719),这串数字是 OSGi 框架为每个 Bundle(可理解为一个插件)分配的唯一标识符,用于区分不同的插件。再后面就是资源在 Bundle 内的相对路径,如 resources/chat.js。 用途:这种地址主要用于在 OSGi 环境中定位 Bundle 内部的资源。在 Eclipse 插件开发中,每个插件都是一个 Bundle,插件内的资源(如 HTML、CSS、JavaScript 文件等)可以通过这种地址来引用,确保资源的独立性和隔离性。
6.2.引用方案 6.2.1.1html,css,js物理不分离即将所有的html,css,js等资源,都写到同一个html文件中,这样就不存在引用问题,代价就是会导致最后html变得很大,如果你开发的插件足够代码多,引用的资源足够多,后期维护变成几百上千行代码的时候,很难维护。这个我就不举例写了。有兴趣自己尝试。
6.2.1.1html,css,jss逻辑不分离相比于上面的方案,物理不分离,我们在物理上将这些资源文件分离。但是在逻辑上不分离,具体措施就是,开发写代码的时候,html,css,各写个的,但是最后代码执行的时候,将css,js,等注入到html,最后,和上面物理不分离的效果是一样的,区别是,上面的代码在开发的时候就已经是同一个文件了,而这个方法,是开发完毕以后,在代码运行的时候,将代码打包合并到一个html文件中。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import org.eclipse.swt.SWT; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.part.ViewPart; public class AiView extends ViewPart { public static final String ID = "com.hutao.search.ai.plugin.aiview"; @Override public void createPartControl(Composite parent) { parent.setLayout(new GridLayout(1, false)); // 创建 Browser 控件 Browser browser = new Browser(parent, SWT.NONE); browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); // 加载 HTML 文件内容 String htmlContent = loadHtmlFile("resources/chat.html"); // 在 Browser 控件中显示 HTML 内容 browser.setText(htmlContent); new CustomFunction(browser, "logToJava"); } /** * @description:加载html页面 * @author:hutao * @mail:hutao1@epri.sgcc * @date:2025年2月18日16:01:01 */ private String loadHtmlFile(String resourcePath) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath); if (inputStream == null) { return "<html><body><h1>无法加载资源</h1></body></html>"; } StringBuilder content = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); return "<html><body><h1>无法加载资源</h1></body></html>"; } // 加载 CSS 文件并内联 String cssContent = loadResourceAsString("resources/chat.css"); String jsContent = loadResourceAsString("resources/chat.js"); // 将 CSS 和 JS 插入到 HTML 内容中 return content.toString() .replace("<link rel=\"stylesheet\" type=\"text/css\" href=\"chat.css\">","<style>" + cssContent + "</style>") .replace("<script src=\"chat.js\"></script>","<script>" + jsContent + "</script>"); } /** * @description:加载资源文件,转成字符串 * @author:hutao * @mail:hutao1@epri.sgcc * @date:2025年2月19日11:01:01 */ private String loadResourceAsString(String resourcePath) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourcePath); if (inputStream == null) { return ""; // 返回空字符串以避免 null } StringBuilder content = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { content.append(line).append("\n"); } } catch (IOException e) { e.printStackTrace(); } return content.toString(); } @Override public void setFocus() { } }至此结束,相信到了这里,你就已经能用前端的开发来开发eclipse的插件了。如果你用的是VUE,也别怕,因为VUE打包最后也是生成HTML,CSS,JS这些代码,只是引用的方式,可能需要做转换
Eclipse插件开发六:使用Web前端技术开发AI助手页面由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Eclipse插件开发六:使用Web前端技术开发AI助手页面”