金山在线文档编辑器

官方文档地址:快速开始-WebOffice 知识库

首先按照文档写的方式将包引入项目了

util.js

javascript 复制代码
import WebOfficeSDK from "../../public/JSEditor/open-jssdk-v0.0.13.umd"
export function WordSDK(url, isEdit, mountDom, isShowTopArea, isShowHeader) {
  const el = document.querySelector(mountDom)
  if (!el) return null
  const instance = WebOfficeSDK.config({
    url,
    mount: el,
    commandBars: [
      {
        cmbId: 'HeaderMiddle', // 组件 ID
        attributes: {
          enable: isEdit, // 禁用组件(组件显示但不响应点击事件)
        },
      },
    ],
    wordOptions: {
      enableReadOnlyComment: isEdit,
      isBestScale: false, // 打开文档时,默认以最佳比例显示
    },
    commonOptions: {
      isShowTopArea, // 隐藏顶部区域(头部和工具栏)
      isShowHeader, // 隐藏头部区域
      // isBrowserViewFullscreen: false, // 是否在浏览器区域全屏
      // isIframeViewFullscreen: false, // 是否在 iframe 区域内全屏
      // acceptVisualViewportResizeEvent: true, // 控制 WebOffice 是否接受外部的 VisualViewport
    },
  });
  instance.setToken({
    // token: `bearer ${getToken('token')}`,
    token: '',
    timeout: 10 * 60 * 1000,
  });
  return instance;
}

封装编辑器组件:KSEditor.vue

javascript 复制代码
<template>
  <div ref="editorContainer" class="editor-container"></div>
</template>

<script>
  import {WordSDK} from "@/utils/util"

  export default {
    name: 'JsEditorComponent',
    props: {
      KSfile: {
        type: String,
        default: ''
      },
      isShowHeader: {
        type: Boolean,
        default: true
      },
      isShowTopArea: {
        type: Boolean,
        default: true
      },
      isEditable: {
        type: Boolean,
        default: true
      }
    },
    data() {
      return {
        editorInstance: null,
        ksConfig: {
          // 在这里配置金山在线文档编辑器的初始化参数
          // 比如 token、文档ID 等
        }
      };
    },
    mounted() {
      // 在组件加载完成后初始化编辑器
      this.initEditor()
    },
    methods: {
      initEditor() {
        let KSInstance = WordSDK(
          this.KSfile,
          this.isEditable,
          '.editor-container',
          this.isShowTopArea,
          this.isShowHeader,
        )
        this.$emit('setInstance', KSInstance)
      },
      createDocument() {
        // 在点击按钮时触发新建文档操作
        // 假设SDK提供了相应的方法来新建文档
        if (this.editor) {
          this.editor.createDocument(); // 假设SDK方法名为createDocument
        } else {
          console.error('编辑器尚未初始化');
        }
      }
    }
  };
</script>

<style scoped>
  /* 可能需要一些样式来控制编辑器容器的宽度和高度 */
  .editor-container {
    width: 100%;
    height: 600px;
  }
</style>

页面testKSEditor.vue

javascript 复制代码
<template>
  <div style="width: 100%">
    <el-upload
      class="compare-uploader"
      ref="upload"
      :with-credentials="true"
      :show-file-list="false"
      withCredentials
      :http-request="httpRequest"
      :on-progress="onProgress"
      :multiple="false"
    >
      <!-- 文件上传-->
      <el-button
        type="ghost"
        slot="trigger"
        size="mini"
      >
        导入文件
      </el-button>
    </el-upload>
    id:<el-input v-model="ksFileInfoCopy.id"/><br/>
    文件类型:<el-input v-model="ksFileInfoCopy.ext"/><br/>
    文件名称:<el-input v-model="ksFileInfoCopy.name"/><br/>
    <el-button
      @click="()=>getFileUrl(this.ksFileInfoCopy.id, this.ksFileInfoCopy.ext)"
    >
      刷新
    </el-button>
    <el-button
      @click="()=>destroyKS()"
    >
      销毁
    </el-button>
    <el-button
      @click="()=>insertBookmark('TEXT')"
    >
      插入书签
    </el-button>
    <el-button
      @click="()=>insertBookmark('TABLE')"
    >
      插入表格类型书签
    </el-button>
    <el-button
      @click="() => this.bookmarks = []"
    >
      清空
    </el-button>
    <el-button
      @click="()=>changeMode()"
    >
      {{mode === 'edit'?'预览':'编辑'}}
    </el-button>
    <el-button
      @click="()=>replaceBookmarkValue()"
    >
      替换书签值
    </el-button>
    <JSEditor
      v-if="showKS"
      @setInstance="setInstance"
      :KSfile="KSfile"
      :isShowTopArea="isShowTopArea"
      :isShowHeader="isShowHeader"
      :isEditable="isEditableFile"
    />
  </div>
</template>

<script>
    import axios from "axios";
    import JSEditor from 'lib@/components/KSEditor'
    import {userTableMap} from "@/modules/testKSeditor/fieldMap";

    const userTable = userTableMap
    export default {
      name: "index",
      components: {
        JSEditor
      },
      data () {
        // supplementRules:
        return {
          userId: this.$store.getters.userId,
          fileLoading: false,
          app: null,
          bookmark: null,
          KSfile: '',
          KSurl: '',
          showKS: false,
          isShowHeader: true,
          isShowTopArea: true,
          isEditableFile: true,
          ksFileInfo: {
            ext: 'docx',
            id: '0af2cc2e1da145f48667a6bd09d30e77',
            // id: '6170f21b9fe94db78bc2e56d8f543c1e',
            name: '工治具报修管理.docx',
            url: ''
          },
          ksFileInfoCopy: {
            ext: 'docx',
            id: '0af2cc2e1da145f48667a6bd09d30e77',
            // id: '6170f21b9fe94db78bc2e56d8f543c1e',
            name: '工治具报修管理.docx',
            url: ''
          },
          tableData: [
            {
              number: '1',
              name: 'yy',
              sex: 'b',
              age: '11'
            },
            {
              number: '2',
              name: 'ee',
              sex: 'g',
              age: '22'
            },
            {
              number: '3',
              name: 'ss',
              sex: 'b',
              age: '33'
            },
            {
              index: '4',
              name: 'ff',
              sex: 'g',
              age: '44'
            },
          ],
          mode: 'edit',
          bookmarks: [],
          KSInstance: null
        }
      },
      methods: {
        async setInstance(instance){
          // console.log(instance, '1111')
          this.KSInstance = instance
          await instance.ready()
          if(this.mode === 'preview'){
            const app = instance.Application
            await app.ActiveDocument.SetReadOnly({
              Value: true
            })
          }
          // console.log(app, 'this.KSInstance.Application')
        },
        httpRequest(fileObj) {
          const formData = new FormData();
          formData.append("file", fileObj.file);
          axios({
            url: 'https://sdap-dev.sunwoda.com/api/platform-oss/public/upload?bucket=sdap',
            method: "post",
            data: formData,
          })
            .then(res => {
              this.ksFileInfo = res.data.datas
              this.ksFileInfoCopy = res.data.datas
              this.getFileUrl(res.data.datas.id, res.data.datas.ext)
            })
            .catch(error => {
              console.log(error);
            });
        },
        getFileUrl(fileId, ext, isPreview) {
          this.showKS = false
          this.fileLoading = true
          axios({
            url: 'https://sdap-dev.sunwoda.com/api/platform-system/public/wps-api/edit-url',
            method: "get",
            params: { fileId, ext, previewMode: 'high_definition' }
          })
            .then(res => {
              this.fileLoading = false
              this.KSfile = res.data.datas
              this.KSurl = res.data.datas
              this.ksFileInfoCopy.url = isPreview ? this.ksFileInfo.url : res.data.datas
              this.ksFileInfo.url = res.data.datas
              this.showKS = true
            })
            .catch(error => {
              console.log(error);
            });
        },
        destroyKS(){
          this.KSInstance.destroy()
        },
        async insertBookmark(type){
          const bookmark = await this.KSInstance.Application.ActiveDocument.Bookmarks;
          const selection = await this.KSInstance.Application.ActiveDocument.ActiveWindow.Selection
          // 区域对象
          let mark = 'mark' + Date.now()
          const range = await selection.Range
          const start = await range.Start
          let content = type + '书签'+ mark
          const end = start + content.length
          // 在选区后面插入内容
          await selection.InsertAfter(content)
          await bookmark.Add({
            Name: mark,
            Range: {
              Start: start,
              End: end
            }
          })
          this.bookmarks.push({
            markName: mark,
            type: type,
            value: mark + '的值'
          })
        },
        changeMode(){
          if(this.mode === 'edit'){
            this.mode = 'preview'
            this.isShowHeader = false
            this.isShowTopArea = false
            this.isEditableFile = false
            let replaceData = []
            let modelLines = this.bookmarks // 合同元素相关信息
            // let allMarks = await this.KSInstance.Application.ActiveDocument.Bookmarks.Json()
            for (const i of modelLines) {
              replaceData.push({
                bookmark: i.markName,
                type: i.type,
                text: i.value
              })
            }

            let template_url = 'https://sdap-dev.sunwoda.com/api/platform-oss/public/download/' + this.ksFileInfo.id
            let data = {
              task_id: this.userId,
              template_url: template_url,
              template_filename: this.ksFileInfo.name,
              use_template_section_property:true,
              flatten_source_style: true,
              sample_list: replaceData
            }
            axios({
              url: 'https://sdap-dev.sunwoda.com/api/platform-system/public/wps-api/wrap-header',
              method: 'post',
              data
            }).then(res => {
              if(res.data.resp_code === 200){
                this.getReviewFile(res.data.datas.download_id)
              }
            })
          } else {
            this.mode = 'edit'
            this.ksFileInfoCopy = this.ksFileInfo
            this.isShowHeader = true
            this.isShowTopArea = true
            this.isEditableFile = true
            this.getFileUrl(this.ksFileInfo.id, this.ksFileInfo.ext)
          }
        },
        getReviewFile(id){
          let data = {
            bucket: 'sdap',
            downloadId: id,
            fileName: id + '测试文档.docx'
          }
          axios({
            url: ' https://sdap-dev.sunwoda.com/api/platform-system/public/wps-api/upload-file',
            method: 'post',
            params: data
          }).then(res => {
            this.ksFileInfoCopy = res.data.datas
            this.getFileUrl(res.data.datas.id, res.data.datas.ext, true)
          })
        },
        async replaceBookmarkValue(){
          let modelLines = this.bookmarks // 合同元素相关信息
          let allMarks = await this.KSInstance.Application.ActiveDocument.Bookmarks.Json()
          let replaceData = []
          for (const i of modelLines) {
            if (i.type === 'TABLE') {
              let tableData = JSON.parse(JSON.stringify(this.tableData))
              let header = ["序号", "姓名", "性别", "年龄"]
              await this.insertTable(header, tableData, userTable, i)
            } else {
              replaceData.push({
                name: i.markName,
                type: 'text',
                value: i.value
              })
            }
          }
          this.KSInstance.Application.ActiveDocument.Bookmarks.ReplaceBookmark(replaceData)
        },
        async insertTable(tableHead, tableBodyData, fieldMap, eleInfo){
          const tables = await this.KSInstance.Application.ActiveDocument.Tables // 编辑器的表格对象
          let tableData = JSON.parse(JSON.stringify(tableBodyData))
          tableData.map((i, ind) => {
            i.number = ind + 1 + ''
          })
          let headerData = {}
          let fields = tableHead.map((elmKey, index) => {
            if (!!elmKey) {
              headerData[fieldMap.get(elmKey)] = elmKey
              return {
                name: elmKey,
                field: fieldMap.get(elmKey)
              }
            }
          })
          tableData.unshift(headerData)
          let rowCount = tableData.length, colCount = tableHead.length
          // 插入表格
          await tables.Add(
            this.KSInstance.Application.ActiveDocument.Bookmarks.Item(eleInfo.markName).Range, // 位置信息
            rowCount, // 新增表格的行数
            colCount, // 新增表格的列数
            1, // 启用自动调整功能
            1 // 根据表格中包含的内容自动调整表格的大小
          )
          const count = await tables.Count  // 表格数量
          const curTable = await tables.Item(count)
          console.log(count, 'count')
          // console.log(tableData, 'tableData')
          // console.log(headerData, 'headerData')
          // console.log(fields, 'fields')
          for (let j = 1; j < rowCount + 1; j++) { // 遍历行
            let row = tableData[j-1]
            for (let f = 1; f < colCount + 1; f++) {  // 遍历列
              let fieldsInfo = fields[f-1]
              // console.log(fieldsInfo, 'fieldsInfo')
              let field = row[fieldsInfo.field] || ''
              const cell = await curTable.Rows.Item(j).Cells.Item(f).Range
              cell.Text = field + ''
            }
          }
          this.KSInstance.Application.ActiveDocument.ReplaceText([ // 将书签的内容替换成空字符串
            {
              key: 'TABLE书签' + eleInfo.markName,
              value: ''
            }
          ])
        }
      }
    }
</script>

<style scoped>

</style>
复制代码
fieldMap.js
javascript 复制代码
export const userTableMap = new Map();
userTableMap.set('序号', "number");
userTableMap.set('姓名', "name");
userTableMap.set("性别", "sex");
userTableMap.set("年龄", "age");

前端代码实现书签替换页面效果:

两张截图是分开截的,效果差不多是这样。是有bug的,给表格赋值不太稳定。书签替换也不稳定,没截到连续的图就是因为这个,老有问题,不想截了,不稳定的原因不明。

还可以调用在线编辑的接口实现书签替换,这种方式不支持替换成表格,但支持替换成在线文档,就是可以建一个在线文档,里面加表格,然后将这个在线文档引入进来。我用的接口是我们的后端封装过的,具体的找金山的询问,毕竟要用这个肯定得买,他们肯定会派人支持。

调用接口的方式,可以在"编辑""预览"之前多次跳转,因为这个方法替换书签不是在原文档上修改,是后台会新建一个文件来替换书签,并且这个新文档会缓存24小时,在这时间内可以下载。

相关推荐
辻戋37 分钟前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保39 分钟前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun2 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp2 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.3 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl5 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫6 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友6 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理8 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻8 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js