农业大数据平台如何配置编辑器支持微信公众号公式导入?
国企项目Word和微信公众号内容导入解决方案
项目背景与需求分析
作为福建某国企的项目负责人,我们近期在企业网站后台管理系统升级中遇到了一项关键需求:需要为文章发布模块增加Word粘贴、Word文档导入以及微信公众号内容粘贴功能。这些功能要求集成到现有的百度UEditor富文本编辑器中,作为插件形式提供,不影响现有系统功能和业务流程。
核心功能需求
- Word粘贴功能:支持从Word复制内容后直接粘贴到网站编辑器,图片自动上传至服务器
- Word文档导入:支持导入Word/Excel/PPT/PDF文档,保留原始样式和内容
- 微信公众号粘贴:自动下载公众号文章图片并上传至服务器
- 云存储支持:当前使用单据存储服务器,未来可升级到主流云存储服务
技术环境要求
- 前端框架:兼容Vue2/Vue3/React
- 编辑器:百度UEditor
- 后端框架:JSP
- 开发工具:Eclipse/MyEclipse/IntelliJ IDEA
- 数据库:MySQL
- 服务器:阿里云ECS/CentOS/RedHat
- 信创环境:支持国产操作系统和浏览器
- CPU架构:x86/ARM/MIPS/LoongArch
解决方案设计
整体架构
我们采用"前端插件+后端服务"的架构模式,确保与现有系统无缝集成:
- 前端插件:基于UEditor扩展开发,提供统一的API接口
- 后端服务:独立部署的文档处理服务,提供RESTful API
- 存储适配层:支持多种存储后端,便于未来扩展
前端实现方案
UEditor插件实现
// word-import-plugin.js
UE.plugins.wordImport = function() {
const editor = this;
// 添加工具栏按钮
editor.addListener('ready', function() {
editor.ui.addButton('wordImport', {
title: '导入Word/微信',
cssRules: 'background-position: -320px -20px;',
onclick: function() {
showImportDialog(editor);
}
});
});
function showImportDialog(editor) {
// 显示导入对话框逻辑
const dialog = new UE.ui.Dialog({
iframeUrl: editor.getOpt('wordImportDialogUrl') || 'word-import.html',
editor: editor,
name: 'wordImport',
title: '导入Word/微信内容',
cssRules: 'width:800px;height:500px;',
buttons: [
{
className: 'edui-okbutton',
label: '确定',
onclick: function() {
dialog.close(true);
}
},
{
className: 'edui-cancelbutton',
label: '取消',
onclick: function() {
dialog.close(false);
}
}
]
});
dialog.render();
dialog.open();
}
// 处理Word粘贴
editor.addListener('beforePaste', function(type, html) {
if (isWordContent(html)) {
return processWordPaste(html);
}
return html;
});
function isWordContent(html) {
// 检测是否为Word内容
return html.includes('mso-') ||
html.includes('urn:schemas-microsoft-com:office:office');
}
async function processWordPaste(html) {
try {
const response = await fetch('/api/word/process', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ content: html })
});
const result = await response.json();
if (result.success) {
return result.data.processedHtml;
}
return html;
} catch (error) {
console.error('Word处理失败:', error);
return html;
}
}
};
微信内容处理组件
// wechat-import.js
export default {
methods: {
async fetchWechatContent(url) {
try {
const response = await this.$http.post('/api/wechat/fetch', { url });
if (response.data.success) {
return response.data.content;
}
throw new Error(response.data.message || '获取微信内容失败');
} catch (error) {
console.error('获取微信内容失败:', error);
throw error;
}
},
async uploadImages(html) {
const imageUrls = this.extractImageUrls(html);
const uploadPromises = imageUrls.map(url =>
this.uploadImage(url).catch(e => {
console.error(`图片上传失败: ${url}`, e);
return null;
})
);
const results = await Promise.all(uploadPromises);
return this.replaceImageUrls(html, results.filter(Boolean));
},
extractImageUrls(html) {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
return Array.from(doc.querySelectorAll('img'))
.map(img => img.src)
.filter(src => src.startsWith('http'));
},
async uploadImage(url) {
const response = await this.$http.post('/api/image/upload', { url });
if (response.data.success) {
return {
originalUrl: url,
newUrl: response.data.url
};
}
throw new Error(response.data.message || '图片上传失败');
},
replaceImageUrls(html, replacements) {
let result = html;
replacements.forEach(replacement => {
result = result.replace(
new RegExp(escapeRegExp(replacement.originalUrl), 'g'),
replacement.newUrl
);
});
return result;
}
}
};
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
后端实现方案
文档处理服务接口
// WordProcessController.java
@RestController
@RequestMapping("/api/word")
public class WordProcessController {
@Autowired
private WordProcessingService wordService;
@Autowired
private ImageStorageService storageService;
@PostMapping("/process")
public ResponseEntity processWordContent(@RequestBody WordProcessRequest request) {
try {
String processedHtml = wordService.processWordHtml(request.getContent());
return ResponseEntity.ok(new ApiResponse(true, "处理成功", processedHtml));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse(false, "处理失败: " + e.getMessage(), null));
}
}
@PostMapping("/import")
public ResponseEntity importWordFile(@RequestParam("file") MultipartFile file) {
try {
String htmlContent = wordService.importWordFile(file);
return ResponseEntity.ok(new ApiResponse(true, "导入成功", htmlContent));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse(false, "导入失败: " + e.getMessage(), null));
}
}
@PostMapping("/import-office")
public ResponseEntity importOfficeFile(@RequestParam("file") MultipartFile file) {
try {
OfficeImportResult result = wordService.importOfficeFile(file);
return ResponseEntity.ok(new ApiResponse(true, "导入成功", result));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse(false, "导入失败: " + e.getMessage(), null));
}
}
}
图片上传服务
// ImageStorageController.java
@RestController
@RequestMapping("/api/image")
public class ImageStorageController {
@Autowired
private ImageStorageService storageService;
@PostMapping("/upload")
public ResponseEntity uploadImage(@RequestBody ImageUploadRequest request) {
try {
String imageUrl = storageService.uploadFromUrl(request.getUrl());
return ResponseEntity.ok(new ApiResponse(true, "上传成功", imageUrl));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse(false, "上传失败: " + e.getMessage(), null));
}
}
@PostMapping("/upload-binary")
public ResponseEntity uploadBinary(@RequestParam("file") MultipartFile file) {
try {
String imageUrl = storageService.uploadBinary(file.getBytes(), file.getOriginalFilename());
return ResponseEntity.ok(new ApiResponse(true, "上传成功", imageUrl));
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse(false, "上传失败: " + e.getMessage(), null));
}
}
}
存储服务实现
// AliyunOssStorageService.java
@Service
public class AliyunOssStorageService implements ImageStorageService {
@Value("${oss.endpoint}")
private String endpoint;
@Value("${oss.accessKeyId}")
private String accessKeyId;
@Value("${oss.accessKeySecret}")
private String accessKeySecret;
@Value("${oss.bucketName}")
private String bucketName;
@Override
public String uploadFromUrl(String imageUrl) throws IOException {
// 下载图片
byte[] imageData = downloadImage(imageUrl);
String fileName = generateFileName(imageUrl);
return uploadToOss(imageData, fileName);
}
@Override
public String uploadBinary(byte[] data, String originalFileName) {
String fileName = generateFileName(originalFileName);
return uploadToOss(data, fileName);
}
private byte[] downloadImage(String imageUrl) throws IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(imageUrl);
try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
HttpEntity entity = response.getEntity();
if (entity != null) {
return EntityUtils.toByteArray(entity);
}
throw new IOException("无法获取图片数据");
}
}
private String uploadToOss(byte[] data, String fileName) {
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
ossClient.putObject(bucketName, fileName, new ByteArrayInputStream(data));
return generateFileUrl(fileName);
} finally {
ossClient.shutdown();
}
}
private String generateFileName(String originalName) {
String extension = originalName.substring(originalName.lastIndexOf("."));
return UUID.randomUUID().toString() + extension;
}
private String generateFileUrl(String fileName) {
return String.format("https://%s.%s/%s", bucketName, endpoint, fileName);
}
}
信创环境兼容性实现
跨平台兼容层设计
为确保在各类信创环境中稳定运行,我们设计了兼容层:
- 浏览器兼容层:
// browser-compat.js
export function isIE8() {
return navigator.userAgent.indexOf('MSIE 8.0') > -1;
}
export function addEvent(element, eventName, handler) {
if (isIE8()) {
element.attachEvent('on' + eventName, handler);
} else {
element.addEventListener(eventName, handler, false);
}
}
export function getComputedStyle(element, property) {
if (isIE8()) {
return element.currentStyle[property];
} else {
return window.getComputedStyle(element, null)[property];
}
}
- 国产操作系统适配:
// OSUtils.java
public class OSUtils {
public static boolean isKylin() {
return System.getProperty("os.name").toLowerCase().contains("kylin");
}
public static boolean isUOS() {
return System.getProperty("os.name").toLowerCase().contains("uos");
}
public static String getFontDir() {
if (isWindows()) {
return "C:\\Windows\\Fonts";
} else if (isKylin() || isUOS()) {
return "/usr/share/fonts";
} else {
return "/usr/local/share/fonts";
}
}
// 其他操作系统检测方法...
}
部署与集成方案
前端集成步骤
- 将插件脚本添加到UEditor插件目录
- 在UEditor配置中添加插件:
// ueditor.config.js
window.UEDITOR_CONFIG = {
// ...其他配置
wordImportDialogUrl: '/static/word-import-dialog.html',
toolbars: [
['wordImport', ...], // 在工具栏中添加按钮
// ...其他工具栏配置
]
};
- 在Vue/React项目中引入组件:
// main.js (Vue2)
import Vue from 'vue';
import WechatImport from './plugins/wechat-import';
Vue.use(WechatImport);
// 或React中使用高阶组件
import { withWechatImport } from './plugins/wechat-import';
const EnhancedComponent = withWechatImport(MyComponent);
后端部署步骤
- 部署文档处理服务
- 配置存储服务参数
- 添加数据库表结构:
CREATE TABLE `sys_document_import` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`original_name` varchar(255) DEFAULT NULL,
`file_size` bigint(20) DEFAULT NULL,
`file_type` varchar(50) DEFAULT NULL,
`storage_path` varchar(500) DEFAULT NULL,
`import_time` datetime DEFAULT CURRENT_TIMESTAMP,
`import_user` varchar(50) DEFAULT NULL,
`status` tinyint(4) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `sys_image_storage` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`original_url` varchar(500) DEFAULT NULL,
`storage_url` varchar(500) DEFAULT NULL,
`storage_type` varchar(50) DEFAULT NULL,
`file_size` bigint(20) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
`create_user` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_original_url` (`original_url`(255)),
KEY `idx_storage_url` (`storage_url`(255))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
商务与技术保障方案
源代码买断方案
基于贵司年项目量500+、预算98万内的要求,我们建议以下方案:
-
源代码买断:98万元一次性买断,包含:
- 全套前后端源代码
- 五年免费技术支持
- 完整技术文档和开发手册
- 开发团队培训服务
-
资质材料:
- 5个以上央企/政府项目合同
- 转账凭证
- 信创环境兼容认证书
- 软件著作权证书
- 营业执照等全套资质
-
知识产权:
- 提供软件著作权转让协议
- 代码所有权完全转移
技术保障措施
- 性能监控:集成APM监控系统
- 安全审计:提供源代码安全审计报告
- 灾备方案:双活数据中心部署建议
- 国产化适配:提供全系列国产化环境测试报告
结语
本方案针对贵司需求提供了完整的Word和微信公众号内容导入解决方案,具有以下优势:
- 无缝集成:作为UEditor插件形式提供,不影响现有系统
- 全面兼容:支持所有信创环境和浏览器
- 自主可控:源代码买断,满足政府和国企要求
- 扩展性强:存储服务可灵活切换,支持未来升级
- 成本优化:一次采购,全集团无限制使用
我们可提供全套源代码和技术支持,确保项目顺利实施并长期稳定运行。
复制插件目录

引入插件文件
UEditor 1.4.3.3示例
注意:不要重复引入jquery,如果您的项目已经引入了jq,则不用再引入jq-1.4
在工具栏中增加插件按钮
//工具栏上的所有的功能按钮和下拉框,可以在new编辑器的实例时选择自己需要的重新定义
toolbars: [
[
"fullscreen",
"source",
"|",
"zycapture",
"|",
"wordpaster","importwordtoimg","netpaster","wordimport","excelimport","pptimport","pdfimport",
"|",
"importword","exportword","importpdf"
]
]
初始化控件

var pos = window.location.href.lastIndexOf("/");
var api = [
window.location.href.substr(0, pos + 1),
"asp/upload.asp"
].join("");
WordPaster.getInstance({
//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203ed
PostUrl: api,
//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936
ImageUrl: "",
//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45
FileFieldName: "file",
//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1
ImageMatch: ''
});//加载控件
注意
如果接口字段名称不是file,请配置FileFieldName。ueditor接口中使用的upfile字段
点击查看详细教程
配置ImageMatch
匹配图片地址,如果服务器返回的是JSON则需要通过正则匹配
ImageMatch: '',
配置ImageUrl
为图片地址增加域名,如果服务器返回的图片地址是相对路径,可通过此属性添加自定义域名。
ImageUrl: "",
配置SESSION
如果接口有权限验证(登陆验证,SESSION验证),请配置COOKIE。或取消权限验证。
参考:http://www.ncmem.com/doc/view.aspx?id=8602DDBF62374D189725BF17367125F3
效果
编辑器界面

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

粘贴Word
一键粘贴Word内容,自动上传Word中的图片,保留文字样式。
Word转图片
一键导入Word文件,并将Word文件转换成图片上传到服务器中。
导入PDF
一键导入PDF文件,并将PDF转换成图片上传到服务器中。
导入PPT
一键导入PPT文件,并将PPT转换成图片上传到服务器中。
上传网络图片

下载示例
更多推荐


所有评论(0)