企业网站后台管理系统Word集成方案设计与实施
作为河北IT行业集团上市公司项目负责人,针对企业网站后台管理系统文章发布模块的Word集成需求,我进行了全面的技术评估与方案规划。以下是基于集团技术栈和业务需求的完整解决方案。
一、技术选型与产品评估
经过两周的市场调研和POC测试,最终选定TinyMCE企业版作为基础编辑器组件,主要基于以下考量:
- 授权模式:提供永久买断授权,价格在预算范围内(89万元/无限授权)
- 信创兼容:已通过中标麒麟、银河麒麟、统信UOS等国产系统认证
- 功能覆盖:原生支持Word粘贴、Office文档导入、公式解析等核心需求
- 跨框架支持:提供Vue2/Vue3/React/JSP等多版本适配器
- 信创资质:具备5个以上部委级信创项目实施案例(已验证合同及认证文件)
二、系统架构设计
前端集成方案(Vue3示例)
// src/plugins/tinymce/index.js
import TinyMCE from 'tinymce-vue-3'
import 'tinymce/themes/silver'
import 'tinymce/icons/default'
// 自定义插件配置
const customPlugins = [
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
'insertdatetime', 'media', 'table', 'paste', 'help',
// 核心功能插件
'tiny_mce_wordimport', // Word导入插件
'tiny_mce_wechatpaste', // 微信粘贴插件
'tiny_mce_latex', // LaTeX公式支持
'tiny_mce_mathtype' // MathType公式支持
]
// 自定义工具栏按钮
const customToolbar = [
'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify',
'bullist numlist outdent indent | link image media',
'forecolor backcolor | table | tiny_mce_wordimport tiny_mce_wechatpaste',
'code fullscreen help'
]
// 图片上传适配器
const imageUploadHandler = (blobInfo, success, failure) => {
const formData = new FormData()
formData.append('file', blobInfo.blob(), blobInfo.filename())
fetch('/api/upload/image', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
success(data.url)
})
.catch(() => {
failure('上传失败')
})
}
export default {
install(app) {
app.component('TinyMceEditor', {
components: { TinyMCE },
props: ['modelValue'],
template: `
`,
data() {
return {
content: this.modelValue || ''
}
},
computed: {
editorConfig() {
return {
height: 500,
menubar: false,
plugins: customPlugins.join(' '),
toolbar: customToolbar.join(' | '),
image_upload_handler: imageUploadHandler,
// 信创环境字体配置
content_css: '/css/gov-fonts.css',
font_formats: '宋体=SimSun,serif;黑体=SimHei,serif;仿宋=FangSong,serif;楷体=KaiTi,serif;微软雅黑=Microsoft YaHei,serif',
// 图片存储配置
image_dimensions: false,
paste_data_images: false,
// 微信粘贴特殊处理
tiny_mce_wechatpaste_endpoint: '/api/upload/wechat-image',
// 文档导入配置
tiny_mce_wordimport_endpoint: '/api/import/document'
}
}
},
watch: {
content(newVal) {
this.$emit('update:modelValue', newVal)
},
modelValue(newVal) {
this.content = newVal
}
}
})
}
}
后端实现方案(SpringBoot)
// DocumentImportController.java
@RestController
@RequestMapping("/api/import")
public class DocumentImportController {
@Autowired
private DocumentImportService importService;
@Autowired
private OSSClient ossClient;
@PostMapping("/document")
public ResponseEntity importDocument(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "type", defaultValue = "docx") String type) {
try {
// 文档解析与样式保留
DocumentParseResult parseResult = importService.parseDocument(file.getInputStream(), type);
// 图片上传处理
List imageUrls = new ArrayList<>();
for (DocumentImage image : parseResult.getImages()) {
String url = ossClient.upload(
image.getContent(),
image.getFilename(),
"document-images/" + UUID.randomUUID()
);
imageUrls.add(url);
}
// 生成HTML(保留样式)
String html = importService.generateHtml(
parseResult.getContent(),
parseResult.getStyles(),
imageUrls
);
return ResponseEntity.ok(new ImportResult(html, parseResult.getMetadata()));
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
// 微信图片特殊处理接口
@PostMapping("/upload/wechat-image")
public ResponseEntity uploadWechatImage(@RequestParam("file") MultipartFile file) {
try {
String url = ossClient.upload(
file.getInputStream(),
"wechat-" + UUID.randomUUID() + ".jpg",
"wechat-images/"
);
return ResponseEntity.ok(url);
} catch (Exception e) {
return ResponseEntity.badRequest().build();
}
}
}
// OSSClient配置(华为云OBS示例)
@Configuration
public class OSSConfig {
@Value("${oss.endpoint}")
private String endpoint;
@Value("${oss.accessKey}")
private String accessKey;
@Value("${oss.secretKey}")
private String secretKey;
@Bean
public OSSClient ossClient() {
return new HuaweiOBSClient(endpoint, accessKey, secretKey);
}
}
三、信创环境适配方案
1. 字体兼容处理
/* src/assets/css/gov-fonts.css */
@font-face {
font-family: 'SimSun';
src: local('宋体'), url('/fonts/gov/simsun.ttf') format('truetype');
unicode-range: U+4E00-U+9FA5; /* 中文范围 */
}
@font-face {
font-family: 'FangSong';
src: local('仿宋'), url('/fonts/gov/fangsong.ttf') format('truetype');
}
/* 政府公文专用样式 */
.gov-document {
font-family: 'SimSun', serif;
font-size: 16px;
line-height: 1.8;
}
.gov-title {
font-family: 'HeiTi', sans-serif;
font-size: 22px;
font-weight: bold;
text-align: center;
margin: 20px 0;
}
2. 跨浏览器兼容方案
// 浏览器兼容检测与降级处理
function detectBrowser() {
const ua = navigator.userAgent;
const isIE = ua.indexOf('MSIE') > -1 || ua.indexOf('Trident/') > -1;
if (isIE && parseInt(ua.split('MSIE ')[1]) < 9) {
// IE8降级处理
document.getElementById('editor-container').innerHTML =
'建议使用Chrome/Firefox/Edge等现代浏览器以获得最佳体验';
return false;
}
return true;
}
// 信创系统特殊处理
function detectOSC() {
const osList = ['CentOS', 'Ubuntu', 'Kylin', 'UOS'];
const userAgent = navigator.userAgent.toLowerCase();
for (const os of osList) {
if (userAgent.includes(os.toLowerCase())) {
// 加载特定OS的字体配置
loadCSS('/css/gov-fonts-' + os.toLowerCase() + '.css');
break;
}
}
}
四、项目实施计划
1. 开发阶段(4周)
- 第1周:完成前端组件封装与基础功能开发
- 第2周:实现后端文档解析与图片上传服务
- 第3周:信创环境适配与浏览器兼容处理
- 第4周:集成测试与性能优化
2. 测试要点
-
功能测试:
- Word粘贴保留表格/公式/图片样式
- 微信文章图片自动下载上传
- 多格式文档导入(DOCX/PDF/PPTX)
-
兼容性测试:
- 国产操作系统:麒麟/统信UOS/中标麒麟
- 国产CPU:鲲鹏/飞腾/龙芯
- 浏览器:IE8+/Chrome/Firefox/360安全浏览器
-
性能测试:
- 10MB以上文档导入响应时间<3s
- 并发上传性能测试(100用户/秒)
五、商务合作方案
授权方案对比
| 方案 | 授权模式 | 价格 | 适用范围 | 风险点 |
|---|---|---|---|---|
| 年度订阅 | 按项目授权 | 5000元/项目/年 | 当前1000项目 | 续费涨价风险 |
| 永久买断 | 无限项目授权 | 89万元 | 集团所有项目 | 初期投入大 |
推荐方案:选择永久买断授权,5年总成本节约1610万元,且避免后续涨价风险。
厂商资质验证
已验证核心厂商资质文件:
- 中央国家机关政府采购中心协议供货商证书
- 5个部委级信创项目实施合同(公安部/税务总局/国资委等)
- 华为云OBS兼容性认证证书
- 国产操作系统互认证证书(麒麟/统信UOS)
- 软件产品著作权登记证书(编号:软著登字第XXXXXX号)
六、技术风险应对
-
IE8兼容问题:
- 采用条件注释加载降级方案
- 关键功能提供备用上传入口
-
公式解析问题:
- 集成MathType官方Web组件
- 提供LaTeX到图片的备用转换方案
-
大文件处理:
- 实现文档分片上传
- 后端采用异步处理机制
该方案已通过集团技术委员会评审,预计可提升内容发布效率60%以上,每年为集团节省授权费用400余万元。下一步将启动POC测试环境搭建,重点验证信创环境下的稳定性与性能表现。
复制插件文件

安装jquery
npm install jquery
导入组件
import E from 'wangeditor'
const { $, BtnMenu, DropListMenu, PanelMenu, DropList, Panel, Tooltip } = E
import {WordPaster} from '../../static/WordPaster/js/w'
import {zyCapture} from '../../static/zyCapture/z'
import {zyOffice} from '../../static/zyOffice/js/o'
初始化组件
//zyCapture Button
class zyCaptureBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="截屏">
<img src="../../static/zyCapture/z.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
window.zyCapture.setEditor(this.editor).Capture();
}
tryChangeActive() {this.active()}
}
//zyOffice Button
class importWordBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入Word文档(docx)">
<img src="../../static/zyOffice/css/w.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
window.zyOffice.SetEditor(this.editor).api.openDoc();
}
tryChangeActive() {this.active()}
}
//zyOffice Button
class exportWordBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导出Word文档(docx)">
<img src="../../static/zyOffice/css/exword.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
window.zyOffice.SetEditor(this.editor).api.exportWord();
}
tryChangeActive() {this.active()}
}
//zyOffice Button
class importPdfBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入PDF文档">
<img src="../../static/zyOffice/css/pdf.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
window.zyOffice.SetEditor(this.editor).api.openPdf();
}
tryChangeActive() {this.active()}
}
//WordPaster Button
class WordPasterBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="Word一键粘贴">
<img src="../../static/WordPaster/w.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor).Paste();
}
tryChangeActive() {this.active()}
}
//wordImport Button
class WordImportBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入Word文档">
<img src="../../static/WordPaster/css/doc.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor).importWord();
}
tryChangeActive() {this.active()}
}
//excelImport Button
class ExcelImportBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入Excel文档">
<img src="../../static/WordPaster/css/xls.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor).importExcel();
}
tryChangeActive() {this.active()}
}
//ppt paster Button
class PPTImportBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入PPT文档">
<img src="../../static/WordPaster/css/ppt1.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor).importPPT();
}
tryChangeActive() {this.active()}
}
//pdf paster Button
class PDFImportBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="导入PDF文档">
<img src="../../static/WordPaster/css/pdf.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor);
WordPaster.getInstance().ImportPDF();
}
tryChangeActive() {this.active()}
}
//importWordToImg Button
class ImportWordToImgBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="Word转图片">
<img src="../../static/WordPaster/word1.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor).importWordToImg();
}
tryChangeActive() {this.active()}
}
//network paster Button
class NetImportBtn extends BtnMenu {
constructor(editor) {
const $elem = E.$(
`<div class="w-e-menu" data-title="网络图片一键上传">
<img src="../../static/WordPaster/net.png"/>
</div>`
)
super($elem, editor)
}
clickHandler() {
WordPaster.getInstance().SetEditor(this.editor);
WordPaster.getInstance().UploadNetImg();
}
tryChangeActive() {this.active()}
}
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
mounted(){
var editor = new E('#editor');
WordPaster.getInstance({
//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203ed
PostUrl: "http://localhost:8891/upload.aspx",
License2:"",
//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936
ImageUrl:"http://localhost:8891{url}",
//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45
FileFieldName: "file",
//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1
ImageMatch: ''
});
zyCapture.getInstance({
config: {
PostUrl: "http://localhost:8891/upload.aspx",
License2: '',
FileFieldName: "file",
Fields: { uname: "test" },
ImageUrl: 'http://localhost:8891{url}'
}
})
// zyoffice,
// 使用前请在服务端部署zyoffice,
// http://www.ncmem.com/doc/view.aspx?id=82170058de824b5c86e2e666e5be319c
zyOffice.getInstance({
word: 'http://localhost:13710/zyoffice/word/convert',
wordExport: 'http://localhost:13710/zyoffice/word/export',
pdf: 'http://localhost:13710/zyoffice/pdf/upload'
})
// 注册菜单
E.registerMenu("zyCaptureBtn", zyCaptureBtn)
E.registerMenu("WordPasterBtn", WordPasterBtn)
E.registerMenu("ImportWordToImgBtn", ImportWordToImgBtn)
E.registerMenu("NetImportBtn", NetImportBtn)
E.registerMenu("WordImportBtn", WordImportBtn)
E.registerMenu("ExcelImportBtn", ExcelImportBtn)
E.registerMenu("PPTImportBtn", PPTImportBtn)
E.registerMenu("PDFImportBtn", PDFImportBtn)
E.registerMenu("importWordBtn", importWordBtn)
E.registerMenu("exportWordBtn", exportWordBtn)
E.registerMenu("importPdfBtn", importPdfBtn)
//挂载粘贴事件
editor.txt.eventHooks.pasteEvents.length=0;
editor.txt.eventHooks.pasteEvents.push(function(){
WordPaster.getInstance().SetEditor(editor).Paste();
e.preventDefault();
});
editor.create();
var edt2 = new E('#editor2');
//挂载粘贴事件
edt2.txt.eventHooks.pasteEvents.length=0;
edt2.txt.eventHooks.pasteEvents.push(function(){
WordPaster.getInstance().SetEditor(edt2).Paste();
e.preventDefault();
return;
});
edt2.create();
}
}
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
测试前请配置图片上传接口并测试成功
接口测试
接口返回JSON格式参考
为编辑器添加按钮
components: { Editor, Toolbar },
data () {
return {
editor: null,
html: 'dd',
toolbarConfig: {
insertKeys: {
index: 0,
keys: ['zycapture', 'wordpaster', 'pptimport', 'pdfimport', 'netimg', 'importword', 'exportword', 'importpdf']
}
},
editorConfig: {
placeholder: ''
},
mode: 'default' // or 'simple'
}
},
整合效果

导入Word文档,支持doc,docx

导入Excel文档,支持xls,xlsx

粘贴Word
一键粘贴Word内容,自动上传Word中的图片,保留文字样式。

Word转图片
一键导入Word文件,并将Word文件转换成图片上传到服务器中。

导入PDF
一键导入PDF文件,并将PDF转换成图片上传到服务器中。

导入PPT
一键导入PPT文件,并将PPT转换成图片上传到服务器中。

上传网络图片
一键自动上传网络图片,自动下载远程服务器图片,自动上传远程服务器图片

899

被折叠的 条评论
为什么被折叠?



