157 lines
3.7 KiB
Plaintext
157 lines
3.7 KiB
Plaintext
<template>
|
|
<drawer-popup
|
|
ref="popupPreviewPage"
|
|
:title="headerTitle"
|
|
direction="bottom"
|
|
height="90%"
|
|
@update-visible="updateVisible"
|
|
>
|
|
<iframe
|
|
class="h-full"
|
|
ref="iframeRef"
|
|
:src="iframeConfig.uri"
|
|
@load="onIframeLoad"
|
|
></iframe>
|
|
</drawer-popup>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import TurndownService from "turndown"; // markdown转html
|
|
import { apiAgentComponentPageResultUpdate } from "@/servers/agentDev";
|
|
import { useI18n } from "@/utils/i18n";
|
|
/**
|
|
* 页面预览组件
|
|
* 用于在弹窗中预览网页内容
|
|
*/
|
|
|
|
// 定义组件props
|
|
interface PreviewData {
|
|
/** 页面 URI */
|
|
uri: string;
|
|
/** 请求方法 */
|
|
method?: "browser_navigate_page" | "browser_open_page";
|
|
/** 数据类型 */
|
|
data_type?: "html" | "markdown";
|
|
/** 请求 ID */
|
|
request_id?: string;
|
|
}
|
|
|
|
// 预览页面弹窗
|
|
const popupPreviewPage = ref<any>(null);
|
|
// iframe引用
|
|
const iframeRef = ref<HTMLIFrameElement>(null);
|
|
|
|
const emit = defineEmits(["update-visible"]);
|
|
|
|
const updateVisible = (value: boolean) => {
|
|
emit("update-visible", value);
|
|
};
|
|
|
|
// iframe配置
|
|
const iframeConfig = reactive<PreviewData>({
|
|
uri: "",
|
|
method: "browser_open_page",
|
|
data_type: "html",
|
|
request_id: "",
|
|
});
|
|
|
|
// 预览页面标题
|
|
const headerTitle = ref<string>("");
|
|
const { t } = useI18n();
|
|
|
|
/**
|
|
* 处理页面打开
|
|
* @param data 预览数据
|
|
*/
|
|
const handlePageOpen = (data: PreviewData) => {
|
|
Object.assign(iframeConfig, data);
|
|
popupPreviewPage.value?.open();
|
|
};
|
|
|
|
/**
|
|
* iframe加载完成事件
|
|
*/
|
|
const onIframeLoad = () => {
|
|
console.log("onIframeLoad");
|
|
const iframe = iframeRef.value;
|
|
if (!iframe || !iframeConfig.uri) return;
|
|
// iframe.src = iframeConfig.uri;
|
|
|
|
onIframeDOMChange(iframe);
|
|
};
|
|
|
|
/**
|
|
* 监听iframe DOM变化
|
|
* @param iframe iframe元素
|
|
*/
|
|
const onIframeDOMChange = (iframe: HTMLIFrameElement) => {
|
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
if (!iframeDoc) return;
|
|
const turndownService = new TurndownService();
|
|
|
|
let timer: NodeJS.Timeout;
|
|
|
|
// 监听 iframe 内部 DOM 变化
|
|
const observer = new MutationObserver(() => {
|
|
// 每次变化后延迟 500ms 再检测,确保渲染稳定
|
|
clearTimeout(timer);
|
|
timer = setTimeout(async () => {
|
|
// 获取 head 中的 title 内容
|
|
const title =
|
|
iframeDoc.querySelector("head > title")?.textContent ||
|
|
t("Mobile.PagePreview.defaultTitle");
|
|
headerTitle.value = title;
|
|
|
|
const html = iframeDoc.body.innerHTML;
|
|
|
|
if (!iframeConfig.method) return;
|
|
// 获取 iframe 内容
|
|
let str = "";
|
|
// 如果是 html
|
|
if (iframeConfig.data_type === "html") {
|
|
str = html;
|
|
}
|
|
// 如果是 markdown
|
|
if (iframeConfig.data_type === "markdown") {
|
|
str = turndownService.turndown(html);
|
|
}
|
|
if (!str) {
|
|
return;
|
|
}
|
|
|
|
if (iframeConfig.method === "browser_navigate_page") {
|
|
const params = {
|
|
requestId: iframeConfig.request_id || "",
|
|
html: str,
|
|
};
|
|
await apiAgentComponentPageResultUpdate(params);
|
|
}
|
|
}, 500);
|
|
});
|
|
|
|
observer.observe(iframeDoc.body, {
|
|
childList: true,
|
|
subtree: true,
|
|
characterData: true,
|
|
});
|
|
|
|
// 清理
|
|
return () => {
|
|
observer.disconnect();
|
|
clearTimeout(timer);
|
|
};
|
|
};
|
|
|
|
// 必须暴露出来,否则父组件访问不到
|
|
defineExpose({
|
|
handlePageOpen,
|
|
popupPreviewPage,
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.h-full {
|
|
border: 2rpx solid #ddd;
|
|
}
|
|
</style>
|