内嵌网页,js注入

This commit is contained in:
2025-04-02 13:55:38 +08:00
parent 6f8ddc1daa
commit cd884686ce
6 changed files with 316 additions and 24 deletions

136
package-lock.json generated
View File

@@ -10,6 +10,7 @@
"dependencies": {
"axios": "^1.8.4",
"core-js": "^3.8.3",
"pinia": "^3.0.1",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"
@@ -2708,6 +2709,30 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/@vue/devtools-kit": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.2.tgz",
"integrity": "sha512-CY0I1JH3Z8PECbn6k3TqM1Bk9ASWxeMtTCvZr7vb+CHi+X/QwQm5F1/fPagraamKMAHVfuuCbdcnNg1A4CYVWQ==",
"license": "MIT",
"dependencies": {
"@vue/devtools-shared": "^7.7.2",
"birpc": "^0.2.19",
"hookable": "^5.5.3",
"mitt": "^3.0.1",
"perfect-debounce": "^1.0.0",
"speakingurl": "^14.0.1",
"superjson": "^2.2.1"
}
},
"node_modules/@vue/devtools-shared": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.2.tgz",
"integrity": "sha512-uBFxnp8gwW2vD6FrJB8JZLUzVb6PNRG0B0jBnHsOH8uKyva2qINY8PTF5Te4QlTbMDqU5K6qtJDr6cNsKWhbOA==",
"license": "MIT",
"dependencies": {
"rfdc": "^1.4.1"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.13",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz",
@@ -3431,6 +3456,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/birpc": {
"version": "0.2.19",
"resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.19.tgz",
"integrity": "sha512-5WeXXAvTmitV1RqJFppT5QtUiz2p1mRSYU000Jkft5ZUCLJIk4uQriYNO50HknxKwM6jd8utNc66K1qGIwwWBQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/bl": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
@@ -4115,6 +4149,21 @@
"dev": true,
"license": "MIT"
},
"node_modules/copy-anything": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
"integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
"license": "MIT",
"dependencies": {
"is-what": "^4.1.8"
},
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/copy-webpack-plugin": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz",
@@ -5927,6 +5976,12 @@
"node": "*"
}
},
"node_modules/hookable": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT"
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
@@ -6447,6 +6502,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-what": {
"version": "4.1.16",
"resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
"integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
"license": "MIT",
"engines": {
"node": ">=12.13"
},
"funding": {
"url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -7248,6 +7315,12 @@
"dev": true,
"license": "ISC"
},
"node_modules/mitt": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
"license": "MIT"
},
"node_modules/module-alias": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.3.tgz",
@@ -7855,6 +7928,12 @@
"node": ">=8"
}
},
"node_modules/perfect-debounce": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
"integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -7874,6 +7953,36 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/pinia": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.1.tgz",
"integrity": "sha512-WXglsDzztOTH6IfcJ99ltYZin2mY8XZCXujkYWVIJlBjqsP6ST7zw+Aarh63E1cDVYeyUcPCxPHzJpEOmzB6Wg==",
"license": "MIT",
"dependencies": {
"@vue/devtools-api": "^7.7.2"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"typescript": ">=4.4.4",
"vue": "^2.7.0 || ^3.5.11"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/pinia/node_modules/@vue/devtools-api": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.2.tgz",
"integrity": "sha512-1syn558KhyN+chO5SjlZIwJ8bV/bQ1nOVTG66t2RbG66ZGekyiYNmRO7X9BJCXQqPsFHlnksqvPhce2qpzxFnA==",
"license": "MIT",
"dependencies": {
"@vue/devtools-kit": "^7.7.2"
}
},
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -9038,6 +9147,12 @@
"node": ">=0.10.0"
}
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},
"node_modules/rimraf": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -9613,6 +9728,15 @@
"wbuf": "^1.7.3"
}
},
"node_modules/speakingurl": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ssri": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
@@ -9726,6 +9850,18 @@
"postcss": "^8.2.15"
}
},
"node_modules/superjson": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
"integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
"license": "MIT",
"dependencies": {
"copy-anything": "^3.0.2"
},
"engines": {
"node": ">=16"
}
},
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",

View File

@@ -9,6 +9,7 @@
"dependencies": {
"axios": "^1.8.4",
"core-js": "^3.8.3",
"pinia": "^3.0.1",
"vue": "^3.2.13",
"vue-router": "^4.0.3",
"vuex": "^4.0.0"

View File

@@ -1,17 +1,25 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>
<%= htmlWebpackPlugin.options.title %>
</title>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@@ -1,8 +1,8 @@
<template>
<div>
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
<!-- <router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> -->
</nav>
<router-view />
</div>

View File

@@ -2,5 +2,14 @@ import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import { createPinia } from 'pinia';
// createApp(App).use(store).use(router).mount('#app')
const app = createApp(App);
app.use(createPinia()); // 注册 Pinia
app.use(store); // 注册 store
app.use(router); // 注册 router
app.mount('#app');
createApp(App).use(store).use(router).mount('#app')

View File

@@ -1,14 +1,152 @@
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App" />
<div>
<button @click="callPython">设置数据"123" Python</button>
<button @click="getPythonData"> Python 获取数据</button>
<button @click="clickChildButton">从tk模拟点击按钮 </button>
<input type="text" id="input" v-model="inputValue"></input>
<div id="output">{{ output }}</div>
<h1>嵌入tk页面</h1>
<iframe id="myIframe" src="https://live-backstage.tiktok.com/portal/overview" width="100%" height="600px"
frameborder="0" ref="myIframe"></iframe>
</div>
</template>
<script setup>
// @ is an alias to /src
import HelloWorld from '@/components/HelloWorld.vue'
import { ref, onMounted } from 'vue';
console.log(process.env.VUE_APP_API_URL)
const myIframe = ref(null);
let inputValue = ref('999');
// 确保 DOM 渲染完成后再访问 iframe
onMounted(() => {
if (myIframe.value) {
// console.log('Iframe mounted:', myIframe.value);
onIframeLoad();
}
});
const onIframeLoad = () => {
if (myIframe.value) {
const iframeDoc = myIframe.value.contentDocument;
// const button = iframeDoc.querySelector('button[data-id="add-host-btn"]')[0];
// if (button) {
// button.click(); // 触发点击事件
// }
console.log('Iframe loaded:', iframeDoc);
if (iframeDoc) {
// console.log('Iframe loaded:', iframeDoc);
// 注入脚本
const script = iframeDoc.createElement('script');
// console.log('script:', script);
script.textContent = `
(function() {
// 拦截 XMLHttpRequest
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url, async, user, password) {
this._method = method; // 保存请求方法
this._url = url; // 保存请求的URL
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function(body) {
if (this._url && this._url.includes('batch_check_anchor')) {
this.addEventListener('load', function() {
console.log('[XHR] 拦截到 batch_check_anchor 请求:');
console.log('URL:', this._url);
console.log('方法:', this._method);
console.log('请求体:', body);
console.log('响应:', this.responseText);
console.log('------------------------');
});
}
return originalSend.apply(this, arguments);
};
// 拦截 fetch
const originalFetch = window.fetch;
window.fetch = async function(...args) {
const [input, init] = args;
const url = typeof input === 'string' ? input : input.url;
if (url && url.includes('batch_check_anchor')) {
console.log('[Fetch] 拦截到 batch_check_anchor 请求:');
console.log('URL:', url);
console.log('方法:', (init && init.method) || 'GET');
console.log('请求体:', (init && init.body) || null);
const response = await originalFetch.apply(this, args);
const clonedResponse = response.clone();
try {
const data = await clonedResponse.json();
console.log('响应:', data);
} catch (e) {
const text = await clonedResponse.text();
console.log('响应:', text);
}
console.log('------------------------');
return response;
}
return originalFetch.apply(this, args);
};
console.log('batch_check_anchor 拦截器已成功注入');
console.log("123123123123132",document.querySelectorAll('button[data-id="view-more"]'))
})();
`;
iframeDoc.body.appendChild(script);
} else {
console.error('无法访问 iframe 的 contentDocument');
}
console.log(123)
} else {
console.error('无法获取 iframe 引用');
}
};
const output = ref('');
const bridge = ref(null);
onMounted(() => {
new QWebChannel(qt.webChannelTransport, function (channel) {
bridge.value = channel.objects.bridge;
});
});
const callPython = () => {
if (bridge.value) {
bridge.value.stringFromJs("123")
}
};
const getPythonData = () => {
if (bridge.value) {
bridge.value.stringToJs(function (result) {
alert(result); // 这里才会显示返回值
});
}
};
const clickChildButton = () => {
const iframeDoc = myIframe.value.contentDocument;
console.log(iframeDoc)
// 查找子页面中的按钮(假设按钮 id 为 child-button
};
</script>