从零开始写一个命令行学生管理系统(CRUD)—— 面向过程的巅峰
2026/6/7 14:48:22
作为河南某国企的项目负责人,我负责的企业网站后台管理系统需要新增以下功能:
经过评估,我推荐采用以下开源组件进行定制开发:
// word-paste-plugin.jsimport{DomElement}from'wangeditor'classWordPastePlugin{constructor(editor){this.editor=editorthis.pasteHandler=this.handlePaste.bind(this)}// 初始化插件init(){consteditor=this.editor editor.config.customPaste=this.pasteHandler// 添加工具栏按钮editor.menus.extend('wordPaste',{class:WordPasteMenu,config:{editor}})}// 处理粘贴事件handlePaste(editor,event){constclipboardData=event.clipboardData||window.clipboardDataif(!clipboardData)return// 处理Word粘贴内容if(clipboardData.types.includes('Files')||clipboardData.types.includes('text/html')){event.preventDefault()this.handleWordPaste(clipboardData,editor)}}// 处理Word粘贴内容asynchandleWordPaste(clipboardData,editor){consthtml=clipboardData.getData('text/html')constfiles=clipboardData.files// 1. 处理图片上传constuploadedImages=awaitthis.uploadImages(files)// 2. 处理HTML内容constcleanedHtml=this.cleanWordHtml(html,uploadedImages)// 3. 插入到编辑器editor.cmd.do('insertHTML',cleanedHtml)}// 上传图片asyncuploadImages(files){constuploadedImages=[]for(leti=0;i<files.length;i++){constfile=files[i]if(file.type.startsWith('image/')){constformData=newFormData()formData.append('file',file)try{constresponse=awaitfetch('/api/upload',{method:'POST',body:formData})constresult=awaitresponse.json()uploadedImages.push({original:file.name,url:result.url})}catch(error){console.error('图片上传失败:',error)}}}returnuploadedImages}// 清理Word生成的HTMLcleanWordHtml(html,images){// 创建一个临时div来解析HTMLconsttempDiv=document.createElement('div')tempDiv.innerHTML=html// 1. 移除Word特有的垃圾代码this.removeWordGarbage(tempDiv)// 2. 替换图片引用this.replaceWordImages(tempDiv,images)// 3. 保留基本样式this.preserveBasicStyles(tempDiv)returntempDiv.innerHTML}// 更多清理方法...}// 工具栏菜单classWordPasteMenu{constructor(editor){this.editor=editorthis.$elem=DomElement.createElement(``)this.type='click'}clickHandler(){// 点击时提示用户直接粘贴Word内容alert('请直接从Word中复制内容,然后在此处粘贴')}tryChangeActive(){// 激活状态}}exportdefaultWordPastePlugin// wechat-paste-plugin.jsclassWechatPastePlugin{constructor(editor){this.editor=editor}init(){document.addEventListener('paste',this.handleWechatPaste.bind(this))}asynchandleWechatPaste(event){constclipboardData=event.clipboardData||window.clipboardDataif(!clipboardData)return// 检测是否是微信公众号内容consthtml=clipboardData.getData('text/html')if(html&&html.includes('mp.weixin.qq.com')){event.preventDefault()awaitthis.processWechatContent(html)}}asyncprocessWechatContent(html){consteditor=this.editorconsttempDiv=document.createElement('div')tempDiv.innerHTML=html// 1. 提取所有图片URLconstimgElements=tempDiv.querySelectorAll('img[data-src]')constimageUrls=Array.from(imgElements).map(img=>img.getAttribute('data-src'))// 2. 下载并上传图片constuploadedImages=[]for(consturlofimageUrls){try{constimageData=awaitthis.downloadImage(url)constuploaded=awaitthis.uploadImage(imageData)uploadedImages.push(uploaded)}catch(error){console.error('图片处理失败:',error)}}// 3. 替换图片URLimgElements.forEach((img,index)=>{if(uploadedImages[index]){img.src=uploadedImages[index].url img.removeAttribute('data-src')}})// 4. 清理并插入内容this.cleanWechatContent(tempDiv)editor.cmd.do('insertHTML',tempDiv.innerHTML)}asyncdownloadImage(url){constresponse=awaitfetch(url)returnawaitresponse.blob()}asyncuploadImage(blob){constformData=newFormData()formData.append('file',blob,'wechat-image.jpg')constresponse=awaitfetch('/api/upload',{method:'POST',body:formData})returnawaitresponse.json()}cleanWechatContent(element){// 移除微信公众号特有的跟踪代码等consttrackers=element.querySelectorAll('[id*="js_content"], [class*="rich_media"]')trackers.forEach(el=>el.removeAttribute('id'))trackers.forEach(el=>el.removeAttribute('class'))}}exportdefaultWechatPastePlugin// doc-import-plugin.jsclassDocImportPlugin{constructor(editor){this.editor=editor}init(){// 添加导入按钮consteditor=this.editor editor.menus.extend('docImport',{class:DocImportMenu,config:{editor,onImport:this.handleFileImport.bind(this)}})}asynchandleFileImport(file){consteditor=this.editor// 显示加载状态editor.showProgressBar(50)try{// 1. 上传文件到服务器constformData=newFormData()formData.append('file',file)constresponse=awaitfetch('/api/doc/import',{method:'POST',body:formData})constresult=awaitresponse.json()// 2. 处理返回的HTML内容if(result.html){editor.cmd.do('insertHTML',result.html)}// 3. 处理返回的图片URL替换if(result.imageMap){this.replaceImportedImages(result.imageMap)}editor.hideProgressBar()}catch(error){console.error('文档导入失败:',error)editor.hideProgressBar()alert('文档导入失败,请检查文件格式')}}replaceImportedImages(imageMap){// 在编辑器内容中查找临时图片引用并替换为实际上传后的URL// 实现略...}}classDocImportMenu{constructor(editor){this.editor=editorthis.$elem=DomElement.createElement(``)this.type='click'// 设置文件输入事件constfileInput=this.$elem.querySelector('#docImportInput')fileInput.addEventListener('change',(e)=>{if(e.target.files.length>0){this.config.onImport(e.target.files[0])e.target.value=''// 允许重复选择同一文件}})}clickHandler(){this.$elem.querySelector('#docImportInput').click()}tryChangeActive(){// 激活状态}}exportdefaultDocImportPlugin// FileUploadServlet.java@WebServlet("/api/upload")@MultipartConfigpublicclassFileUploadServletextendsHttpServlet{privatestaticfinalStringOSS_ENDPOINT="your-oss-endpoint";privatestaticfinalStringOSS_BUCKET="your-oss-bucket";privatestaticfinalStringOSS_ACCESS_KEY="your-access-key";privatestaticfinalStringOSS_SECRET_KEY="your-secret-key";protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{response.setContentType("application/json");response.setCharacterEncoding("UTF-8");try{PartfilePart=request.getPart("file");if(filePart==null||filePart.getSize()==0){response.getWriter().write("{\"success\":false,\"message\":\"未接收到文件\"}");return;}// 获取文件信息StringfileName=getFileName(filePart);StringfileExt=fileName.substring(fileName.lastIndexOf(".")+1).toLowerCase();// 生成唯一文件名StringuniqueName=UUID.randomUUID().toString()+"."+fileExt;// 上传到OSSStringfileUrl=uploadToOSS(filePart.getInputStream(),uniqueName);// 返回结果response.getWriter().write(String.format("{\"success\":true,\"url\":\"%s\",\"original\":\"%s\"}",fileUrl,fileName));}catch(Exceptione){response.getWriter().write(String.format("{\"success\":false,\"message\":\"%s\"}",e.getMessage()));}}privateStringgetFileName(Partpart){StringcontentDisp=part.getHeader("content-disposition");String[]items=contentDisp.split(";");for(Strings:items){if(s.trim().startsWith("filename")){returns.substring(s.indexOf("=")+2,s.length()-1);}}return"";}privateStringuploadToOSS(InputStreaminputStream,StringobjectName)throwsException{// 创建OSSClient实例OSSossClient=newOSSClientBuilder().build(OSS_ENDPOINT,OSS_ACCESS_KEY,OSS_SECRET_KEY);try{// 上传文件流ossClient.putObject(OSS_BUCKET,objectName,inputStream);// 生成访问URL(根据配置决定是否生成带签名的URL)// 这里简单返回对象Key,前端可以根据需要拼接完整URLreturnobjectName;}finally{ossClient.shutdown();}}}// DocImportServlet.java@WebServlet("/api/doc/import")@MultipartConfigpublicclassDocImportServletextendsHttpServlet{protectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)throwsServletException,IOException{response.setContentType("application/json");response.setCharacterEncoding("UTF-8");try{PartfilePart=request.getPart("file");if(filePart==null||filePart.getSize()==0){response.getWriter().write("{\"success\":false,\"message\":\"未接收到文件\"}");return;}// 获取文件信息StringfileName=getFileName(filePart);StringfileExt=fileName.substring(fileName.lastIndexOf(".")+1).toLowerCase();// 临时保存文件PathtempFile=Files.createTempFile("docimport-","."+fileExt);try(InputStreamfileContent=filePart.getInputStream()){Files.copy(fileContent,tempFile,StandardCopyOption.REPLACE_EXISTING);}// 根据文件类型处理DocImportResultresult;switch(fileExt){case"doc":case"docx":result=processWordDocument(tempFile.toString());break;case"xls":case"xlsx":result=processExcelDocument(tempFile.toString());break;case"ppt":case"pptx":result=processPowerPointDocument(tempFile.toString());break;case"pdf":result=processPdfDocument(tempFile.toString());break;default:thrownewException("不支持的文件格式");}// 返回处理结果response.getWriter().write(newGson().toJson(result));}catch(Exceptione){response.getWriter().write(String.format("{\"success\":false,\"message\":\"%s\"}",e.getMessage()));}}privateDocImportResultprocessWordDocument(StringfilePath)throwsException{// 使用Apache POI或docx4j处理Word文档// 这里简化为示例代码DocImportResultresult=newDocImportResult();result.setSuccess(true);// 1. 提取文本内容StringBuilderhtml=newStringBuilder();html.append("");html.append("导入的Word文档标题");html.append("这里是文档的正文内容...");html.append("");result.setHtml(html.toString());// 2. 提取图片并上传MapimageMap=newHashMap<>();// 假设从文档中提取了图片并上传imageMap.put("image1.tmp","image1.jpg");imageMap.put("image2.tmp","image2.jpg");result.setImageMap(imageMap);returnresult;}// 其他文档处理方法...privatestaticclassDocImportResult{privatebooleansuccess;privateStringhtml;privateMapimageMap;// getters and setterspublicbooleanisSuccess(){returnsuccess;}publicvoidsetSuccess(booleansuccess){this.success=success;}publicStringgetHtml(){returnhtml;}publicvoidsetHtml(Stringhtml){this.html=html;}publicMapgetImageMap(){returnimageMap;}publicvoidsetImageMap(MapimageMap){this.imageMap=imageMap;}}}-- 文件上传记录表CREATETABLE`file_upload_record`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`file_name`varchar(255)NOTNULLCOMMENT'原始文件名',`file_path`varchar(512)NOTNULLCOMMENT'存储路径或对象Key',`file_size`bigint(20)NOTNULLCOMMENT'文件大小(字节)',`file_type`varchar(50)NOTNULLCOMMENT'文件类型',`upload_time`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'上传时间',`uploader`varchar(100)DEFAULTNULLCOMMENT'上传人',`storage_type`varchar(20)NOTNULLCOMMENT'存储类型(OSS/本地等)',`md5`varchar(64)DEFAULTNULLCOMMENT'文件MD5',PRIMARYKEY(`id`),KEY`idx_file_path`(`file_path`),KEY`idx_upload_time`(`upload_time`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='文件上传记录表';-- 文档导入记录表CREATETABLE`doc_import_record`(`id`bigint(20)NOTNULLAUTO_INCREMENT,`original_file_name`varchar(255)NOTNULLCOMMENT'原始文件名',`file_type`varchar(20)NOTNULLCOMMENT'文件类型',`import_time`datetimeNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'导入时间',`importer`varchar(100)DEFAULTNULLCOMMENT'导入人',`status`tinyint(4)NOTNULLCOMMENT'状态(0-处理中,1-成功,2-失败)',`error_msg`textDEFAULTNULLCOMMENT'错误信息',PRIMARYKEY(`id`),KEY`idx_import_time`(`import_time`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COMMENT='文档导入记录表';importEditorfrom'wangeditor'importWordPastePluginfrom'./plugins/word-paste-plugin'importWechatPastePluginfrom'./plugins/wechat-paste-plugin'importDocImportPluginfrom'./plugins/doc-import-plugin'// 注册插件Editor.use(WordPastePlugin)Editor.use(WechatPastePlugin)Editor.use(DocImportPlugin)// 创建编辑器实例consteditor=newEditor('#editor-container')editor.create()export default { mounted() { this.initEditor() }, methods: { initEditor() { const editor = new this.$editor('#editor-container') editor.config.uploadImgServer = '/api/upload' editor.config.uploadFileName = 'file' editor.config.showLinkImg = false editor.config.showFullScreen = true // 初始化插件 editor.plugins.get('wordPaste').init() editor.plugins.get('wechatPaste').init() editor.plugins.get('docImport').init() editor.create() this.editor = editor } }, beforeDestroy() { if (this.editor) { this.editor.destroy() } } }// polyfill.jsimport'core-js/stable'import'regenerator-runtime/runtime'// 检测IEif(navigator.userAgent.indexOf('MSIE')>-1||!!document.documentMode){// IE特定处理window.isIE=true// 加载IE专用polyfillimport('ie-array-find-polyfill').then(()=>{console.log('IE polyfill loaded')})}// 检测信创浏览器constisQianxin=navigator.userAgent.includes('Qianxin')constisLotus=navigator.userAgent.includes('Lotus')if(isQianxin||isLotus){// 信创浏览器特定处理console.log('Running in Qianxin or Lotus browser')}// os-detect.jsexportfunctiondetectOS(){constuserAgent=navigator.userAgent.toLowerCase()if(userAgent.includes('windows nt')){return'windows'}elseif(userAgent.includes('mac os x')){return'macos'}elseif(userAgent.includes('linux')){if(userAgent.includes('centos'))return'centos'if(userAgent.includes('redhat'))return'redhat'if(userAgent.includes('ubuntu'))return'ubuntu'if(userAgent.includes('uos'))return'uos'if(userAgent.includes('kylin')){if(userAgent.includes('mips')||userAgent.includes('loongarch')){return'kylin-loongson'}else{return'kylin'}}return'linux'}return'unknown'}源代码交付:
培训内容:
支持服务:
预算分配:
采购建议:
本方案提供了完整的Word粘贴、文档导入和微信公众号内容处理功能实现,满足以下要求:
该方案可以直接集成到现有系统中,不影响现有业务流程,用户使用简单方便,通过工具栏按钮即可完成所有操作。
安装jquery
npm install jqueryimportEfrom'wangeditor'const{$,BtnMenu,DropListMenu,PanelMenu,DropList,Panel,Tooltip}=Eimport{WordPaster}from'../../static/WordPaster/js/w'import{zyCapture}from'../../static/zyCapture/z'import{zyOffice}from'../../static/zyOffice/js/o'//zyCapture ButtonclasszyCaptureBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyCapture.setEditor(this.editor).Capture();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportWordBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openDoc();}tryChangeActive(){this.active()}}//zyOffice ButtonclassexportWordBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.exportWord();}tryChangeActive(){this.active()}}//zyOffice ButtonclassimportPdfBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){window.zyOffice.SetEditor(this.editor).api.openPdf();}tryChangeActive(){this.active()}}//WordPaster ButtonclassWordPasterBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).Paste();}tryChangeActive(){this.active()}}//wordImport ButtonclassWordImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWord();}tryChangeActive(){this.active()}}//excelImport ButtonclassExcelImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importExcel();}tryChangeActive(){this.active()}}//ppt paster ButtonclassPPTImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importPPT();}tryChangeActive(){this.active()}}//pdf paster ButtonclassPDFImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor);WordPaster.getInstance().ImportPDF();}tryChangeActive(){this.active()}}//importWordToImg ButtonclassImportWordToImgBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor).importWordToImg();}tryChangeActive(){this.active()}}//network paster ButtonclassNetImportBtnextendsBtnMenu{constructor(editor){const$elem=E.$(`<div class="w-e-menu">`)super($elem,editor)}clickHandler(){WordPaster.getInstance().SetEditor(this.editor);WordPaster.getInstance().UploadNetImg();}tryChangeActive(){this.active()}}exportdefault{name:'HelloWorld',data(){return{msg:'Welcome to Your Vue.js App'}},mounted(){vareditor=newE('#editor');WordPaster.getInstance({//上传接口:http://www.ncmem.com/doc/view.aspx?id=d88b60a2b0204af1ba62fa66288203edPostUrl:"http://localhost:8891/upload.aspx",License2:"",//为图片地址增加域名:http://www.ncmem.com/doc/view.aspx?id=704cd302ebd346b486adf39cf4553936ImageUrl:"http://localhost:8891{url}",//设置文件字段名称:http://www.ncmem.com/doc/view.aspx?id=c3ad06c2ae31454cb418ceb2b8da7c45FileFieldName:"file",//提取图片地址:http://www.ncmem.com/doc/view.aspx?id=07e3f323d22d4571ad213441ab8530d1ImageMatch:''});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=82170058de824b5c86e2e666e5be319czyOffice.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();varedt2=newE('#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:010px;}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内容,自动上传Word中的图片,保留文字样式。
一键导入Word文件,并将Word文件转换成图片上传到服务器中。
一键导入PDF文件,并将PDF转换成图片上传到服务器中。
一键导入PPT文件,并将PPT转换成图片上传到服务器中。
一键自动上传网络图片,自动下载远程服务器图片,自动上传远程服务器图片
点击下载完整示例