效果

核心思想

监听onWindowSelectionChange事件,获取当前光标位置,然后对比高连位置 判断是否选在区域内。

若是则激活右侧对应的高亮面板

核心代码

index.vue

<script setup lang="ts">
import RightPanel from './right-panel.vue';
import WpsPanel from './wps-panel.vue';
import emitter from '../../utils/emitter';
import { ref, onBeforeMount } from 'vue'; const props = defineProps<{ fileId: string }>(); onBeforeMount(() => {
emitter.on('wpsReady', (params) => {
jssdk.value = params;
});
}); const jssdk = ref<any>(null);
</script> <template>
<div class="wps-render">
<div class="wps">
<wps-panel :fileId="props.fileId" />
</div>
<div class="right" v-if="jssdk">
<right-panel :fileId="props.fileId" :jssdk="jssdk" />
</div>
</div>
</template> <style lang="less">
.wps-render {
height: 100vh;
display: flex;
.wps {
width: 70%;
}
.right {
width: 30%;
}
}
</style>

right-panel.vue

<script setup lang="ts">
import { ref, onBeforeMount, computed } from 'vue';
import axios from 'axios';
import _ from 'lodash'; const props = defineProps<{ fileId: string; jssdk: any }>(); onBeforeMount(async () => {
await synncCheckData();
props.jssdk.on('WindowSelectionChange', _.debounce(onWindowSelectionChange, 500));
}); /*
* 监听光标移动
*/
const onWindowSelectionChange = async (event: any) => {
console.log(event);
for (const iterator of checkData.value) {
for (const risk of iterator.risks) {
for (const hlight of risk.highlight_list) {
const atBegin = event.begin >= hlight.begin && event.begin <= hlight.end;
const atEnd = event.end >= hlight.begin && event.end <= hlight.end;
// 假设目标光标or所选区域 在高亮范围内,则匹配的上
if (atBegin || atEnd) {
hlight.active = true;
} else {
hlight.active = false;
}
}
}
}
}; //定义计算属性,返回wpsApp
const wpsApp: any = computed(() => {
return props.jssdk.Application;
}); // 审核结果
const checkData = ref<any>([]); /*
* 获取审查结果new
*/
const synncCheckData = async () => {
const url = 'primaryApi/api/contract_review/v1/parse_result';
const res = await axios.post(url, { id: props.fileId, role: '甲方' });
// 获取审查结果 高亮里补充坐标信息
for (const iterator of res.data.data.danger_list) {
for (const risk of iterator.risks) {
for (const hlight of risk.highlight_list) {
if (hlight.text) {
const res = await findByText(hlight.text);
if (res) {
hlight.begin = res.begin;
hlight.end = res.end;
}
}
}
}
}
checkData.value = res.data.data.danger_list;
}; /*
* 根据文本查找内容位置坐标
*/ const findByText = async (text: string) => {
// 1. 搜索
const findResult: Array<any> = await wpsApp.value.ActiveDocument.Find.Execute(text, false);
// 2. 获取位置信息
if (findResult.length === 0) {
return false;
}
const { pos: begin, len } = findResult[0];
const end = begin + len;
return { begin, end };
}; /*
* 点击右侧面板
*/
const onClick = async (item: any) => {
const res = await findByText(item.text);
if (!res) {
return false;
} console.log('res', res); // // 1. 获取选中区域 https://wwo.wps.cn/docs/front-end/API/Word/Range
// const range = await wpsApp.value.ActiveDocument.Range(res.begin, res.end);
// // 2. 选中区域设置高亮
// range.HighlightColorIndex = 7;
// 3. 获取区域对象
const range = await wpsApp.value.ActiveDocument.Range.SetRange(res.begin, res.end);
// 4. 滚动文档窗口, 显示指定的区域
await wpsApp.value.ActiveDocument.ActiveWindow.ScrollIntoView(range);
};
</script> <template>
<div class="right-panel">
<div v-for="item in checkData" :key="item.danger_num" class="item">
<div class="label">{{ item.label }} ({{ item.danger_num }})</div>
<div class="content">
<div v-for="risk in item.risks" class="risk">
<div class="rlabel">{{ risk.label }}</div>
<div class="rcontent">
<div v-for="hlight in risk.highlight_list" class="hlight" @click="onClick(hlight)" :class="{ active: hlight.active }">
{{ hlight.text }}
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="less">
.right-panel {
height: 100vh;
overflow: auto;
.item {
border: red solid 2px;
margin-top: 10px;
border-radius: 4px;
min-height: 100px;
.label {
background-color: aliceblue;
padding: 10px;
}
.content {
padding: 10px;
.risk {
background-color: bisque;
margin-bottom: 10px;
padding: 10px;
.rcontent {
.hlight {
color: red;
cursor: pointer;
border: greenyellow solid 2px;
margin-bottom: 10px;
padding: 10px;
}
.active {
background-color: red;
color: white;
}
}
}
}
}
}
</style>

wps-panel.vue

<script setup lang="ts">
import { ref, onBeforeMount } from 'vue';
import WebOfficeSDK from 'web-office-sdk';
import axios from 'axios';
import emitter from '../../utils/emitter'; const props = defineProps<{ fileId: string }>(); onBeforeMount(() => {
init();
}); const init = async () => {
// 获取wps中url与token get_url_token:,
const {
data: { wpsUrl: url, token }
} = await axios.get('/wpsApi/getUrlAndToken', {
params: { fileid: props.fileId }
});
const jssdk = WebOfficeSDK.config({
url,
mount: document.querySelector('.wps-box') as any // 挂载到div
}); // 设置 token
jssdk.setToken({
token: token,
hasRefreshTokenConfig: false
}); // 打开文档结果
jssdk.on('fileOpen', (data) => {
// console.log(123, data.success);
}); // 等待加载完毕
jssdk.ready().then(() => {
emitter.emit('wpsReady', jssdk);
}); // 如果需要对 iframe 进行特殊的处理,可以通过以下方式拿到 iframe 的 dom 对象
// console.log(jssdk.iframe);
};
</script> <template>
<div class="hello-world">
<div class="wps-box"></div>
</div>
</template>
<style lang="less">
.hello-world {
.wps-box {
height: 100vh;
}
}
</style>

wps office定制化 自定义右侧面板 关联批注的更多相关文章

  1. strapi系列--如何自定义非界面化的接口,定制化自己的业务逻辑

    为什么要进行后端定制呢? 在实际开发过程中,项目中有些需求是不需要创建界面化接口的,需要我们定制化自己的业务逻辑,那么我们该如何处理这个需求呢?本文以图文并茂的形式,定制一个我们自己的业务逻辑接口. ...

  2. Django 2.0 学习(10):Django 定制化

    定制化admin表单 通过使用admin.site.register(Question)注册Question模型,Django可以构造默认的表单.通常,可以通过对象的注册机制来告诉Django我们想要 ...

  3. python 全栈开发,Day114(装饰器,排序规则,显示列,添加按钮,定制ModelForm,自定义列表页面,自定制URL)

    一.装饰器 装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象. 装饰器的应用场景:比如插入日志,性能测试,事务处理, ...

  4. Oceanus:美团HTTP流量定制化路由的实践

    背景简述 Oceanus是美团基础架构部研发的统一HTTP服务治理框架,基于Nginx和ngx_lua扩展,主要提供服务注册与发现.动态负载均衡.可视化管理.定制化路由.安全反扒.session ID ...

  5. solr特点三: 基于Solr实现排序定制化参考

    排序实现有N种形式,最低成本.最快响应时间是目标 一份索引,支持N种排序策略并且在线互不干扰是要考虑的每一种实现,处理的场景是不同的,不要千篇一律 020排序,从索引到效果,有不少坑,这篇文章没有细说 ...

  6. Centos7.5的定制化安装

    一.前言 关于定制化centos7.5的镜像真的是历经波折,前前后后.来来回回尝试了不少于20次,上网找了各种关于定制7系统的方法,都没有成功... 但最终功夫不负有心人终于解决了,O(∩_∩)O哈哈 ...

  7. Gradle 实现 Android 多渠道定制化打包

    Gradle 实现 Android 多渠道定制化打包 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在项目中遇到需要实现 Apk 多渠道.定制化打包, Google .百度查找了一些资料, ...

  8. Oracle Sales Cloud:管理沙盒(定制化)小细节1——利用公式创建字段并显示在前端页面

    Oracle Sales Cloud(Oracle 销售云)是一套基于Oracle云端的CRM管理系统.由于 Oracle 销售云是基于 Oracle 云环境的,它与传统的管理系统相比,显著特点之一便 ...

  9. cobbler重装、web、定制化

    cobbler重装 根据此文已自动化安装centos 7的版本http://www.cnblogs.com/shhnwangjian/p/5858900.html 在cobbler-test主机上重装 ...

  10. WPS Office文档未保存怎么恢复

    有时候用WPS Office时,文档还没保存,因为电脑卡死或者关机,再次打开时编辑的内容都不见了,这个时候可以利用WPS自带的备份功能来恢复文档,表格.幻灯片.文档都是可以的. 首先单击WPS左上角的 ...

随机推荐

  1. Chrome 135 版本新特性

    Chrome 135 版本新特性 一.Chrome 135 版本浏览器更新 ** 1. 第三方托管账户注册迁移到 OIDC 授权码流程** Chrome 135 将账户注册的登录页面从营销网站迁移到动 ...

  2. 【Java异步编程利器】CompletableFuture完全指南

    Java异步编程利器:CompletableFuture完全指南 图:传统同步 vs 异步编程模式对比 一.为什么需要异步编程? 同步 vs 异步的餐厅点餐比喻 同步方式 异步方式 顾客站在柜台前等待 ...

  3. 详细介绍Java的SPI机制

    一.什么是SPI机制 SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用,比如java.s ...

  4. python获取指定文件夹内文件名称

    比如下图,文件夹内有若干文件,且文件夹路径:C:\Users\Administrator\Desktop\2147\1024 4行代码,解决问题 import os path = "C:\\ ...

  5. 蒟蒻 AstralNahida 的码风

    前言 这里是蒟蒻 OIer AstralNahida 在 OI 中的码风的详细介绍. 个人认为码风相当清晰,供给各位参考. 约定 对于一些表示必要性的关键词,从 must 到 mustn't 排序如下 ...

  6. Graphpad Prism10.1.2 中文版科学绘图软件 安装包下载

    Prism10中文版下载链接: https://pan.baidu.com/s/18a0_uLi3ANWC3KxlHOzZAA?pwd=6666 提取码: 6666 Graphpad Prism 是一 ...

  7. 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案

    今天在使用国产数据库金仓时,我发现每次遇到问题都习惯性地打开金仓社区进行搜索和查看相关信息.可是每次打开浏览器的操作总让我觉得有些麻烦,于是我决定不再依赖这种繁琐的过程.索性今天我把这个接口提取出来, ...

  8. JS中常用方法

    数组 splice: splice() 方法的第一个参数是起始索引,第二个参数是要删除的元素数量(可以为0),然后可以传递更多的参数作为要插入的新元素. 限制删除的数量: const arr = [1 ...

  9. JTextField限制输入数据类型(java GUI)

    package javaBasic; import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Co ...

  10. eclipse从安装到配置Tomcat及运行helloworld

    eclipse安装 0.下载 官方地址https://www.eclipse.org/downloads/packages/ 下载那个版本大家参见下图自行取舍叭 1.安装 解压缩 推荐的解压工具7-z ...