金山在线文档编辑器

官方文档地址:快速开始-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小时,在这时间内可以下载。

相关推荐
崔庆才丨静觅5 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax