当前位置: 首页 > news >正文

Eclipse插件开发六:使用Web前端技术开发AI助手页面

之前的过程中,我们都不怎么熟悉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.com.cn
     * @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.com.cn
     * @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.com.cn
     * @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这些代码,只是引用的方式,可能需要做转换

相关文章:

  • JDK最详细安装教程,零基础入门到精通,收藏这篇就够了
  • 网络原理-
  • springboot pagehelper分页插件封装
  • 【Bert】自然语言(Language Model)入门之---Bert
  • 企业内部知识库:安全协作打造企业智慧运营基石
  • leetcode 2585. 获得分数的方法数
  • C/C++ 格式化输出( unsigned long long)
  • vue stores全局状态共享
  • alphafold3本地部署
  • 划分字母区间
  • 【Qt】常用控件(一)
  • 【练习】【二分】力扣热题100 153. 寻找旋转排序数组中的最小值
  • C++ Qt建立一个HTTP服务器
  • 鸿蒙开发:V2版本装饰器之@Monitor装饰器
  • 阐解WiFi信号强度
  • Linux centOS7 bash编程小技巧
  • Vue3中的setup
  • Linux应用之构建命令行解释器(bash进程)
  • vue3之echarts柱状图-圆锥加自动轮播
  • 使用Termux将安卓手机变成随身AI服务器(page assist连接)
  • 新能源车盈利拐点:8家上市车企去年合计净利854亿元,多家扭亏
  • 零食连锁鸣鸣很忙递表港交所:去年营收393亿元,门店超1.4万家,净利润率2.1%
  • 中国人保一季度业绩“分化”:财险净利增超92%,寿险增收不增利
  • 哈莉·贝瑞、洪常秀等出任戛纳主竞赛单元评委
  • 北美票房|《罪人》遭媒体唱衰,好莱坞业内人士集体反击
  • 酒店就“保洁员调包住客港币”致歉,称希望尽早达成解决方案