wps office定制化 自定义右侧面板 关联批注
效果
核心思想
监听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>
分类:
javaScript