|
@@ -0,0 +1,424 @@
|
|
|
+<template>
|
|
|
+ <div class="assistance-edit-container">
|
|
|
+ <div class="edit-container-rich-text">
|
|
|
+ <froala
|
|
|
+ id="froala-editor"
|
|
|
+ ref="froalaEditor"
|
|
|
+ :tag="'textarea'"
|
|
|
+ :config="froalaConfig"
|
|
|
+ v-model="addDocForm.Content"
|
|
|
+ ></froala>
|
|
|
+ </div>
|
|
|
+ <div class="right-area">
|
|
|
+ <div class="save-time" v-if="modifyTime">
|
|
|
+ 最近保存时间:{{ modifyTime }}
|
|
|
+ </div>
|
|
|
+ <div class="edit-container-document-options">
|
|
|
+ <div class="document-options-button-box">
|
|
|
+ <el-button type="primary" class="document-options-button" @click="previewDocument" v-loading="isSubmiting">预览</el-button>
|
|
|
+ <el-button type="primary" class="document-options-button" @click="saveDocument('保存')" v-loading="isSubmiting">保存</el-button>
|
|
|
+ <el-button type="primary" class="document-options-button" @click="saveDocument('发布')" v-loading="isSubmiting">发布</el-button>
|
|
|
+ </div>
|
|
|
+ <div class="document-options-form">
|
|
|
+ <el-form :model="addDocForm" ref="addDocForm" :rules="addDocRules">
|
|
|
+ <el-form-item label="文章标题" prop="Title">
|
|
|
+ <el-input v-model="addDocForm.Title" placeholder="请输入文章标题"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="所属分类" prop="ClassifyId">
|
|
|
+ <el-cascader style="width: 100%;"
|
|
|
+ v-model="addDocForm.ClassifyId" :options="classifyList"
|
|
|
+ :props="{value:'ClassifyId',label:'ClassifyName',children:'Children',emitPath:false,disabled:'Disabled'}" placeholder="所属分类"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="文章作者" prop="Author">
|
|
|
+ <el-input v-model="addDocForm.Author" placeholder="请输入文章作者"></el-input>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="相关推荐">
|
|
|
+ <div v-for="(item,index) in addDocForm.RecommendData" :key="index" class="form-item-recommendedLink">
|
|
|
+ <el-input v-model="item.Name" placeholder="请输入链接名称" style="width: 190px;"></el-input>
|
|
|
+ <div class="recommendedLink-line"></div>
|
|
|
+ <el-input v-model="item.Url" placeholder="请输入链接" style="width: 190px;"></el-input>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import {assistanceDocInterence} from "@/api/api.js"
|
|
|
+import {createBottomHref} from "./utils/common"
|
|
|
+ export default {
|
|
|
+ name:"assistanceDocAdd",
|
|
|
+ data() {
|
|
|
+ const that = this;
|
|
|
+ return {
|
|
|
+ editor: null,
|
|
|
+ froalaConfig: {
|
|
|
+ toolbarButtons: [
|
|
|
+ "insertImage",
|
|
|
+ "insertVideo",
|
|
|
+ "embedly",
|
|
|
+ "insertFile",
|
|
|
+ "textColor",
|
|
|
+ "bold",
|
|
|
+ "italic",
|
|
|
+ "underline",
|
|
|
+ "strikeThrough",
|
|
|
+ "subscript",
|
|
|
+ "superscript",
|
|
|
+ "fontFamily",
|
|
|
+ "fontSize",
|
|
|
+ "color",
|
|
|
+ "inlineClass",
|
|
|
+ "inlineStyle",
|
|
|
+ "paragraphStyle",
|
|
|
+ "lineHeight",
|
|
|
+ "paragraphFormat",
|
|
|
+ "align",
|
|
|
+ "formatOL",
|
|
|
+ "formatUL",
|
|
|
+ "outdent",
|
|
|
+ "indent",
|
|
|
+ "quote",
|
|
|
+ "insertTable",
|
|
|
+ "emoticons",
|
|
|
+ "fontAwesome",
|
|
|
+ "specialCharacters",
|
|
|
+ "insertHR",
|
|
|
+ "selectAll",
|
|
|
+ "clearFormatting",
|
|
|
+ "html",
|
|
|
+ "undo",
|
|
|
+ "redo"
|
|
|
+ ],
|
|
|
+ height:"calc(100vh - 230px)",
|
|
|
+ fontSize: ["12", "14", "16", "18", "20", "24", "28", "32", "36", "40"],
|
|
|
+ fontSizeDefaultSelection: "16",
|
|
|
+ theme: "dark", //主题
|
|
|
+ placeholderText: "请输入内容",
|
|
|
+ language: "zh_cn", //国际化
|
|
|
+ imageUploadURL: process.env.API_ROOT + "/report/uploadImg", //上传url
|
|
|
+ videoUploadURL: process.env.API_ROOT + "/report/uploadImg", //上传url
|
|
|
+ fileUploadURL: process.env.API_ROOT + "/report/uploadImg", //上传url 更多上传介绍 请访问https://www.froala.com/wysiwyg-editor/docs/options
|
|
|
+ imageEditButtons:['imageAlign', 'imageCaption', 'imageRemove', '-', 'imageDisplay', 'imageSize'],
|
|
|
+ quickInsertButtons: ["image","video","hr"], //快速插入项
|
|
|
+ quickInsertEnabled:false, // 是否启用快速插入功能
|
|
|
+ toolbarVisibleWithoutSelection: false, //是否开启 不选中模式
|
|
|
+ // disableRightClick:true,//是否屏蔽右击
|
|
|
+ toolbarSticky: false, //操作栏是否自动吸顶
|
|
|
+ // zIndex:99999,
|
|
|
+ saveInterval: 0,
|
|
|
+ /* saveParam: 'content',
|
|
|
+ saveURL: process.env.API_ROOT+'/report/saveReportContent',
|
|
|
+ saveMethod: 'POST',
|
|
|
+ saveParams: {}, */
|
|
|
+ events: {
|
|
|
+ //this.editor 定义在vue data 中
|
|
|
+ initialized: function () {
|
|
|
+ that.editor = this;
|
|
|
+ },
|
|
|
+ keyup: function (e, editor) {
|
|
|
+ //添加事件,在每次按键按下时,都记录一下最后停留位置
|
|
|
+ that.$nextTick(function () {
|
|
|
+ that.lastEditRange = getSelection().getRangeAt(0);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ click: function (e, editor) {
|
|
|
+ //添加事件,在每次鼠标点击时,都记录一下最后停留位置
|
|
|
+ that.$nextTick(function () {
|
|
|
+ that.lastEditRange = getSelection().getRangeAt(0);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ classifyList:[],
|
|
|
+ addDocForm:{
|
|
|
+ Title:"",
|
|
|
+ ClassifyId:"",
|
|
|
+ Author:"",
|
|
|
+ Status:1,
|
|
|
+ Content:'',
|
|
|
+ AnchorData:[],
|
|
|
+ RecommendData:[{Name:"",Url:""},{Name:"",Url:""}],
|
|
|
+ },
|
|
|
+ addDocRules:{
|
|
|
+ Title:{required:true,message:'文章标题不能为空',trigger:'blur'},
|
|
|
+ ClassifyId:{required:true,message:'文章所属分类不能为空',trigger:'change'},
|
|
|
+ Author:{required:true,message:'文章作者不能为空',trigger:'blur'}
|
|
|
+ },
|
|
|
+ anchorData:[],
|
|
|
+ isSubmiting:false,
|
|
|
+ autoSaveTimer:null,
|
|
|
+ modifyTime:'',
|
|
|
+ contentChange:false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch:{
|
|
|
+ addDocForm:{
|
|
|
+ handler:function(){
|
|
|
+ console.log('内容改变');
|
|
|
+ this.contentChange=true
|
|
|
+ },
|
|
|
+ deep:true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.getClassifyData()
|
|
|
+ if(this.$route.query.DocId){
|
|
|
+ assistanceDocInterence.getAssistanceDoc({DocId:this.$route.query.DocId}).then(res=>{
|
|
|
+ if(res.Ret == 200){
|
|
|
+ this.addDocForm={
|
|
|
+ Id:res.Data.Id,
|
|
|
+ Title:res.Data.Title,
|
|
|
+ ClassifyId:res.Data.ClassifyId,
|
|
|
+ Author:res.Data.Author,
|
|
|
+ Status:res.Data.Status,
|
|
|
+ Content:res.Data.Content,
|
|
|
+ RecommendData:res.Data.Recommend || [{Name:"",Url:""},{Name:"",Url:""}]
|
|
|
+ }
|
|
|
+ this.modifyTime=res.Data.ModifyTime
|
|
|
+ if((!this.autoSaveTimer) && this.addDocForm.Id){
|
|
|
+ this.autoSaveTimer=setInterval(()=>{
|
|
|
+ this.saveDocument('保存',true)
|
|
|
+ },6000)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ destroyed(){
|
|
|
+ clearInterval(this.autoSaveTimer)
|
|
|
+ this.autoSaveTimer=null
|
|
|
+ },
|
|
|
+ mounted(){
|
|
|
+ this.addDocForm.Content=""
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ getClassifyData(){
|
|
|
+ assistanceDocInterence.getAssistanceClassifyList().then(res=>{
|
|
|
+ if(res.Ret == 200){
|
|
|
+ this.classifyList = res.Data?res.Data.AllNodes||[]:[]
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 生成锚点
|
|
|
+ generateAnchor(){
|
|
|
+ this.anchorData=[]
|
|
|
+ // 搜索富文本中的h1和h2标签 当做一级和二级的锚点
|
|
|
+ this.searchTitleTag(0,1)
|
|
|
+ // console.log(this.addDocForm.Content,this.anchorData);
|
|
|
+ },
|
|
|
+ // 搜索标题标签h1,h2
|
|
|
+ searchTitleTag(searchPosition,firstLevel){
|
|
|
+ let frontH1Posiiton,nextH1Posiiton,H2Posiiton=0
|
|
|
+ let frontH1RightPosiiton,H2RightPosiiton=0
|
|
|
+ let backH1Posiiton,backH2Posiiton=0
|
|
|
+ // 本次搜索第一个h1的位置
|
|
|
+ frontH1Posiiton = this.addDocForm.Content.indexOf('<h1',searchPosition)
|
|
|
+ // 右闭合标签
|
|
|
+ frontH1RightPosiiton = this.addDocForm.Content.indexOf('>',frontH1Posiiton)
|
|
|
+
|
|
|
+ if(frontH1Posiiton == -1) return
|
|
|
+
|
|
|
+ let anchorText=`id="doc_anchor_${firstLevel}"`
|
|
|
+ // console.log(frontH1Posiiton,firstLevel,'firstLevel');
|
|
|
+ this.addDocForm.Content = this.addDocForm.Content.substring(0, frontH1Posiiton+3)
|
|
|
+ +" "+anchorText + this.addDocForm.Content.substring(frontH1RightPosiiton);
|
|
|
+ // 再次获取右闭合标签
|
|
|
+ frontH1RightPosiiton = this.addDocForm.Content.indexOf('>',frontH1Posiiton)
|
|
|
+ // 对应的</h1>的位置 原本用的</h1>,后来发现> 和 </h1>之间会掺其他标签
|
|
|
+ backH1Posiiton = this.addDocForm.Content.indexOf('<',frontH1RightPosiiton)
|
|
|
+ // 获取标题
|
|
|
+ let AnchorTitle = this.addDocForm.Content.substring(frontH1RightPosiiton+1,backH1Posiiton)
|
|
|
+
|
|
|
+ this.anchorData.push({
|
|
|
+ AnchorId:`${firstLevel}`,
|
|
|
+ Anchor:`doc_anchor_${firstLevel}`,
|
|
|
+ AnchorName:AnchorTitle,
|
|
|
+ Child:[]})
|
|
|
+ // 本次搜索下一个h1的位置
|
|
|
+ nextH1Posiiton = this.addDocForm.Content.indexOf('<h1',backH1Posiiton)==-1?
|
|
|
+ this.addDocForm.Content.length:this.addDocForm.Content.indexOf('<h1',backH1Posiiton)
|
|
|
+ // 从第一个h1的位置开始查找h2标签
|
|
|
+ H2Posiiton = this.addDocForm.Content.indexOf('<h2',backH1Posiiton)
|
|
|
+
|
|
|
+ let secondLevel=1
|
|
|
+ while (!(H2Posiiton==-1 || H2Posiiton>nextH1Posiiton)) {
|
|
|
+ // 右闭合标签
|
|
|
+ H2RightPosiiton = this.addDocForm.Content.indexOf('>',H2Posiiton)
|
|
|
+
|
|
|
+ // 找到了,并且位置小于下一个h1的位置
|
|
|
+ let anchorTextH2=`id="doc_anchor_${firstLevel}_${secondLevel}"`
|
|
|
+ // console.log(H2Posiiton,secondLevel,'secondLevel');
|
|
|
+ this.addDocForm.Content = this.addDocForm.Content.substring(0, H2Posiiton+3)
|
|
|
+ +" "+anchorTextH2 + this.addDocForm.Content.substring(H2RightPosiiton);
|
|
|
+ // 再次获取右闭合标签
|
|
|
+ H2RightPosiiton = this.addDocForm.Content.indexOf('>',H2Posiiton)
|
|
|
+ // 对应的</h2>的位置 原本用的</h2>,后来发现> 和 </h2>之间会掺其他标签
|
|
|
+ backH2Posiiton = this.addDocForm.Content.indexOf('<',H2RightPosiiton)
|
|
|
+ // 获取标题
|
|
|
+ let AnchorTitleLevelTwo = this.addDocForm.Content.substring(H2RightPosiiton+1,backH2Posiiton)
|
|
|
+ this.anchorData[firstLevel-1].Child.push(
|
|
|
+ {AnchorId:`${firstLevel}_${secondLevel}`,
|
|
|
+ Anchor:`doc_anchor_${firstLevel}_${secondLevel}`,
|
|
|
+ AnchorName:AnchorTitleLevelTwo,
|
|
|
+ Child:[]
|
|
|
+ })
|
|
|
+ // nextH1Posiiton 和 secondLevel 随之增加
|
|
|
+ // nextH1Posiiton +=anchorTextH2.length+1
|
|
|
+ // 更新nextH1Posiiton位置
|
|
|
+ nextH1Posiiton = this.addDocForm.Content.indexOf('<h1',backH1Posiiton)==-1?
|
|
|
+ this.addDocForm.Content.length:this.addDocForm.Content.indexOf('<h1',backH1Posiiton)
|
|
|
+ secondLevel++
|
|
|
+ H2Posiiton = this.addDocForm.Content.indexOf('<h2',backH2Posiiton)
|
|
|
+ }
|
|
|
+ // 结束一轮 <h1></h1>标签的寻找
|
|
|
+ if(this.addDocForm.Content.indexOf('<h1',backH1Posiiton+4)!=-1){
|
|
|
+ // 如果有下一个h1的标签,说明寻找还没结束,继续寻找
|
|
|
+ firstLevel++
|
|
|
+ this.searchTitleTag(nextH1Posiiton,firstLevel)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ previewDocument(){
|
|
|
+ if(this.isSubmiting) return
|
|
|
+ if(!this.addDocForm.Content){
|
|
|
+ this.$message.error("文章内容不能为空")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ let bottomLink = createBottomHref(this.addDocForm.RecommendData)
|
|
|
+
|
|
|
+ sessionStorage.setItem("documentDoc",this.addDocForm.Content+bottomLink)
|
|
|
+ let { href } = this.$router.resolve({ path: "/assistanceDocDetail" });
|
|
|
+ window.open(href, "_blank");
|
|
|
+ },
|
|
|
+ saveDocument(type,isAuto){
|
|
|
+ if(this.isSubmiting) return
|
|
|
+ this.$refs.addDocForm.validate(valid=>{
|
|
|
+ if(valid){
|
|
|
+ if(!this.addDocForm.Content){
|
|
|
+ this.$message.error("文章内容不能为空")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if(!isAuto) this.isSubmiting=true
|
|
|
+ if(type=="发布") this.addDocForm.Status=2
|
|
|
+
|
|
|
+ this.generateAnchor()
|
|
|
+ this.addDocForm.AnchorData = this.anchorData
|
|
|
+ //保存
|
|
|
+ assistanceDocInterence.addAssistanceDoc({...this.addDocForm,IsChange:this.contentChange}).then(res=>{
|
|
|
+ if(res.Ret == 200){
|
|
|
+ this.contentChange=false
|
|
|
+
|
|
|
+ !isAuto && this.$message({
|
|
|
+ type:'success',
|
|
|
+ message:'操作成功',
|
|
|
+ duration:1000
|
|
|
+ })
|
|
|
+ if(type=="发布"){
|
|
|
+ setTimeout(()=>{
|
|
|
+ this.$router.back()
|
|
|
+ },1000)
|
|
|
+ }else{
|
|
|
+ this.isSubmiting=false
|
|
|
+ this.modifyTime=res.Data.ModifyTime
|
|
|
+ if(!this.addDocForm.Id){
|
|
|
+ //新增
|
|
|
+ setTimeout(()=>{
|
|
|
+ this.$router.replace("/assistanceDocAdd?DocId="+res.Data.HelpDocId)
|
|
|
+ this.addDocForm.Id = res.Data.HelpDocId
|
|
|
+ if((!this.autoSaveTimer) && this.addDocForm.Id){
|
|
|
+ this.autoSaveTimer=setInterval(()=>{
|
|
|
+ this.saveDocument('保存',true)
|
|
|
+ },6000)
|
|
|
+ }
|
|
|
+ },1000)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch(()=>{
|
|
|
+ this.isSubmiting=false
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .assistance-edit-container{
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-start;
|
|
|
+ .edit-container-rich-text{
|
|
|
+ flex-grow: 1;
|
|
|
+ min-height: calc(100vh - 110px);
|
|
|
+ background-color: white;
|
|
|
+ border:solid 1px #ECECEC;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+ .right-area{
|
|
|
+ margin-left: 20px;
|
|
|
+ min-height: calc(100vh - 112px);
|
|
|
+ .save-time{
|
|
|
+ font-size: 16px;
|
|
|
+ line-height: 22px;
|
|
|
+ color: #000000;
|
|
|
+ font-weight: 400;
|
|
|
+ background-color: unset;
|
|
|
+ margin-bottom: 10px;
|
|
|
+ }
|
|
|
+ .edit-container-document-options{
|
|
|
+ min-height: calc(100% - 32px);
|
|
|
+ background-color: white;
|
|
|
+ border:solid 1px #ECECEC;
|
|
|
+ box-sizing: border-box;
|
|
|
+ width: 440px;
|
|
|
+ min-width: 440px;
|
|
|
+ .document-options-button-box{
|
|
|
+ width: 100%;
|
|
|
+ height: 80px;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 20px;
|
|
|
+ box-shadow: 0px 5px 10px #ECECEC;
|
|
|
+ border-bottom: solid 1px #ECECEC;
|
|
|
+ .document-options-button{
|
|
|
+ height: 40px;
|
|
|
+ width: 120px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .document-options-form{
|
|
|
+ padding: 30px 20px;
|
|
|
+ .form-item-recommendedLink{
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-start;
|
|
|
+ width: 100%;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ &:last-child{
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ .recommendedLink-line{
|
|
|
+ flex: 1;
|
|
|
+ height: 1px;
|
|
|
+ background-color:#DCDFE6 ;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|
|
|
+<style lang="scss">
|
|
|
+.assistance-edit-container{
|
|
|
+ .fr-toolbar,.fr-box.fr-basic .fr-wrapper{
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+}
|
|
|
+.fr-popup.fr-active{
|
|
|
+ z-index: 100000!important;
|
|
|
+ opacity: 1!important;
|
|
|
+}
|
|
|
+</style>
|