2026-03-16 18:46:16 +08:00
|
|
|
|
<template>
|
2026-03-17 14:43:08 +08:00
|
|
|
|
<div v-if="!expire" ref="containerRef" class="!w-full !h-full z-999" :class="noPage ? 'absolute top-0 left-0' : 'pos-fixed top-0 left-0'"></div>
|
|
|
|
|
|
<div v-else class="flex justify-center items-center w-full h-full">
|
|
|
|
|
|
<div class="my-60px text-center">
|
|
|
|
|
|
链接已失效!
|
|
|
|
|
|
<span v-if="!noPage" class="cursor-pointer color-#0085FF ml-10px" @click="home">返回首页</span>
|
|
|
|
|
|
<span v-else class="block mt-10px color-#999">文档预览加载失败</span>
|
2026-03-16 18:46:16 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<!-- <Modal ref="modalRef" class="max-w-80%" width="1340px" v-model:open="open" :footer="null" title="在线资源">
|
|
|
|
|
|
|
|
|
|
|
|
<div class="flex min-h-600px bg-#f5f5f5 flex-col ">
|
|
|
|
|
|
<div class="bg-white p-10px">
|
|
|
|
|
|
<a-input-search allowClear class="w-full max-w-360px ml-5px" v-model:value="searchName" placeholder="请输入作者"
|
|
|
|
|
|
enter-button="搜索" size="large" @search="getImgs" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex flex-grow flex-wrap pt-15px pl-15px">
|
|
|
|
|
|
<div v-for="item in dataSource"
|
|
|
|
|
|
class="mr-15px mb-15px w-200px max-w-200px max-h-260px bg-white p-6px flex flex-col rounded-10px">
|
|
|
|
|
|
|
|
|
|
|
|
<div class="">{{ item.name }}</div>
|
|
|
|
|
|
<img class=" object-contain my-auto cursor-pointer" alt="资源过期了" :src="item.url + img_resize"
|
|
|
|
|
|
@click="setImgPreview(item.url)" />
|
|
|
|
|
|
<div class="flex justify-around mt-5px" style="">
|
|
|
|
|
|
<a-button class="mx-auto my-5px" type="primary" @click="setImgPreview(item.url)"> 查看</a-button>
|
|
|
|
|
|
<a-button class="mx-auto my-5px" type="primary" @click="addImg(item)" :loading="loading_add">
|
|
|
|
|
|
使用</a-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="flex mx-15px mb-15px bg-white py-10px" v-if="pagination">
|
|
|
|
|
|
<div style="margin-left: auto;margin-right: 12px;">
|
|
|
|
|
|
<Pagination v-model:current="pagination.current" @change="paginationChange" @showSizeChange="pageSizeChange"
|
|
|
|
|
|
:showSizeChanger="true" :total="pagination.total" v-model:pageSize="pagination.pageSize" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</Modal> -->
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<!-- 阿里云IMM weboffice -->
|
|
|
|
|
|
<script lang="ts" name="WebOffice" setup>
|
2026-03-17 14:43:08 +08:00
|
|
|
|
import { onMounted, ref, nextTick, onUnmounted, onBeforeUnmount } from 'vue';
|
2026-03-16 18:46:16 +08:00
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
generateWebofficeToken,
|
|
|
|
|
|
generateWebofficeTokenReadOnly,
|
|
|
|
|
|
refreshWebofficeToken,
|
2026-03-16 19:35:31 +08:00
|
|
|
|
} from '@/api/imm.api';
|
2026-03-16 18:46:16 +08:00
|
|
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
|
|
|
|
import { getTemItem, TemObj } from './temObjs';
|
|
|
|
|
|
|
2026-03-17 14:43:08 +08:00
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
/** 嵌入模式:与页面共存,使用 absolute 定位 */
|
|
|
|
|
|
noPage?: boolean;
|
|
|
|
|
|
/** 文档 URL(嵌入模式必传) */
|
|
|
|
|
|
url?: string;
|
|
|
|
|
|
/** 文件名(嵌入模式,用于 IMM) */
|
|
|
|
|
|
fileName?: string;
|
|
|
|
|
|
/** 文件 ID(嵌入模式可选) */
|
|
|
|
|
|
fileId?: string;
|
|
|
|
|
|
}>();
|
|
|
|
|
|
|
2026-03-16 20:06:56 +08:00
|
|
|
|
const containerRef = ref<HTMLElement | null>(null);
|
2026-03-16 18:46:16 +08:00
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
const expire = ref(false);
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
let updateSizeInterval: any;
|
2026-03-17 14:43:08 +08:00
|
|
|
|
|
|
|
|
|
|
function getTemObjFromProps(): TemObj | null {
|
|
|
|
|
|
if (!props.url) return null;
|
|
|
|
|
|
const ext = props.url.split('.').pop()?.split('?')[0] || 'pdf';
|
|
|
|
|
|
const name = props.fileName?.includes('.') ? props.fileName : `${props.fileName || 'document'}.${ext}`;
|
|
|
|
|
|
return {
|
|
|
|
|
|
id: props.fileId || '',
|
|
|
|
|
|
isEdit: false,
|
|
|
|
|
|
name,
|
|
|
|
|
|
url: encodeURIComponent(props.url),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-16 18:46:16 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
init(containerRef.value);
|
2026-03-17 14:43:08 +08:00
|
|
|
|
});
|
2026-03-16 18:46:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
const _temObj = ref<TemObj>({
|
|
|
|
|
|
id: '',
|
|
|
|
|
|
isEdit: false,
|
|
|
|
|
|
name: '',
|
|
|
|
|
|
url: '',
|
|
|
|
|
|
});
|
|
|
|
|
|
const onUnmountedUpdateSize = ref(true);
|
|
|
|
|
|
const updateSize = async () => {
|
|
|
|
|
|
if (!onUnmountedUpdateSize.value) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
onBeforeUnmount(() => {
|
|
|
|
|
|
updateSize();
|
|
|
|
|
|
onUnmountedUpdateSize.value = false;
|
|
|
|
|
|
clearInterval(updateSizeInterval);
|
|
|
|
|
|
clearTimeout(timer.value);
|
|
|
|
|
|
})
|
|
|
|
|
|
const timer = ref<any>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const debouncedFn = (time = 1000) => {
|
|
|
|
|
|
clearTimeout(timer.value);
|
|
|
|
|
|
timer.value = setTimeout(() => {
|
|
|
|
|
|
updateSize();
|
|
|
|
|
|
}, time);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
|
window.removeEventListener('beforeunload', updateSize);
|
|
|
|
|
|
})
|
|
|
|
|
|
window.addEventListener('beforeunload', updateSize);
|
|
|
|
|
|
|
|
|
|
|
|
function home() {
|
|
|
|
|
|
router.replace("/datas");
|
|
|
|
|
|
}
|
|
|
|
|
|
const baseInstance = ref<any>(null);
|
2026-03-16 20:06:56 +08:00
|
|
|
|
async function init(mount: HTMLElement | null) {
|
2026-03-16 18:46:16 +08:00
|
|
|
|
if (!mount) {
|
|
|
|
|
|
console.error('确保挂载节点元素存在。 一般在 onMounted 钩子中调用。');
|
2026-03-17 14:43:08 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let temObj: TemObj | null;
|
|
|
|
|
|
if (props.noPage && props.url) {
|
|
|
|
|
|
temObj = getTemObjFromProps();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
temObj = getTemItem(route.query._t as string);
|
2026-03-16 18:46:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!temObj) {
|
|
|
|
|
|
expire.value = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_temObj.value = temObj;
|
|
|
|
|
|
|
2026-03-16 20:06:56 +08:00
|
|
|
|
const url = decodeURIComponent(`oss://lesingle-kid-course${new URL(decodeURIComponent(temObj.url)).pathname}`);
|
2026-03-16 18:46:16 +08:00
|
|
|
|
let tokenInfo = await getTokenFun(url, temObj);
|
|
|
|
|
|
const instance = (window as any).aliyun.config({
|
|
|
|
|
|
mount,
|
|
|
|
|
|
url: tokenInfo.webofficeURL,
|
|
|
|
|
|
refreshToken: () => {
|
|
|
|
|
|
// timeout过期时刷新 token
|
|
|
|
|
|
// return props.refreshTokenFun(tokenInfo).then((data) => {
|
|
|
|
|
|
return refreshTokenFun(tokenInfo).then((data) => {
|
|
|
|
|
|
Object.assign(tokenInfo, data);
|
|
|
|
|
|
return {
|
|
|
|
|
|
token: tokenInfo.accessToken,
|
2026-03-16 20:06:56 +08:00
|
|
|
|
timeout: 10 * 60 * 1000,
|
2026-03-16 18:46:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
baseInstance.value = instance;
|
|
|
|
|
|
instance.setToken({
|
|
|
|
|
|
token: tokenInfo.accessToken,
|
2026-03-16 20:06:56 +08:00
|
|
|
|
timeout: 10 * 60 * 1000,
|
2026-03-16 18:46:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
await instance.ready();
|
|
|
|
|
|
// const imgurl = 'http://image.activity.lesingle.com/activitymaterial/poster/%E5%8A%A8%E7%89%A9%E7%BB%98%E6%9C%AC_1736908271102.jpg';
|
|
|
|
|
|
// const imgurl = 'https://img0.baidu.com/it/u=3217812679,2585737758&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500';
|
|
|
|
|
|
// const app = instance.Application;
|
|
|
|
|
|
// if (app && app.ActiveDocument) {
|
|
|
|
|
|
// await insertWordImage(instance, imgurl);
|
|
|
|
|
|
// } else if (app && app.ActivePresentation) {
|
|
|
|
|
|
// await insertPPTImage(instance, imgurl);
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// if (temObj.isEdit) {
|
|
|
|
|
|
// const app = instance.Application;
|
|
|
|
|
|
// // 获取 InsertTab 标签页下的所有控件
|
|
|
|
|
|
// const controls = await app.CommandBars('InsertTab').Controls;
|
|
|
|
|
|
|
|
|
|
|
|
// const newButton = await controls.Add(1);//:添加一个按钮控件,其中1表示按钮类型。
|
|
|
|
|
|
// newButton.Caption = '在线图片';//:设置按钮的标题。
|
|
|
|
|
|
// newButton.OnAction = () => open.value = true;//:设置按钮点击事件的回调函数。
|
|
|
|
|
|
// newButton.Picture = imgstr;
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// } catch (error) {
|
|
|
|
|
|
// console.error('下拉框', error)
|
|
|
|
|
|
// }
|
|
|
|
|
|
instance.on('fileStatus', () => {
|
|
|
|
|
|
debouncedFn(5000);
|
|
|
|
|
|
});
|
2026-03-17 14:43:08 +08:00
|
|
|
|
instance.ApiEvent.AddApiEventListener('error', (err: unknown) => {
|
2026-03-16 18:46:16 +08:00
|
|
|
|
console.log('发生错误:', err);
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取IMM凭证信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function getTokenFun(url: any, temObj: TemObj) {
|
|
|
|
|
|
let res = temObj.isEdit
|
|
|
|
|
|
? await generateWebofficeToken({ url: url, name: temObj.name })
|
|
|
|
|
|
: await generateWebofficeTokenReadOnly({ url: url, name: temObj.name });
|
|
|
|
|
|
return res
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 刷新IMM凭证信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function refreshTokenFun(tokenInfo: any) {
|
|
|
|
|
|
console.log('refreshWebofficeToken is called');
|
|
|
|
|
|
let res = await refreshWebofficeToken({ accessToken: tokenInfo.accessToken, refreshToken: tokenInfo.refreshToken });
|
|
|
|
|
|
return res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
2026-03-16 20:06:56 +08:00
|
|
|
|
<style scoped></style>
|