feat: 创建多智能体展示分支

- 包含会展策划智能体配置文件
- 包含项目文档和配置更新
- 准备多智能体协作功能展示
This commit is contained in:
Yep_Q
2025-09-26 15:05:20 +08:00
parent 2fcaf21842
commit fc10d180be
2229 changed files with 403271 additions and 1 deletions

View File

@@ -0,0 +1,548 @@
const { createApp } = Vue;
const treeNode = {
name: 'tree-node',
props: {
node: {
type: Object,
default: []
}
},
template: `
<div class="w-full pb-[4px]">
<div class="node" @click.stop="toggle(node)">
<div v-if="node.folderFlag" @click.stop="toggle(node)" :style="{backgroundColor:selectId === node.uuid?'#E4EFFF':''}" class="h-32px flex items-center cursor-pointer rounded-[6px] hover:bg-[#E4EFFF]">
<i v-if="isExpanded" style="margin-left:4px" class="mr-10px fas fa-caret-down arrow"></i>
<i v-else style="margin-left:4px" class="mr-10px fas fa-caret-right arrow"></i>
<svg class="text-[12px] mr-10px" width="1em" height="1em" fill="currentColor"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1091 1024">
<path d="M0 932.552719V91.461901Q0 53.489228 27.290422 26.588669 54.190982 0 92.241628 0H412.475241a38.986318 38.986318 0 0 1 29.785547 13.801156l121.715283 143.859513h435.399196q38.050646 0 65.029178 26.588668 27.290422 26.900559 27.290422 64.951206v683.274203q0 38.128619-27.290422 64.951205-26.978532 26.588669-65.029178 26.588669H92.3196q-38.050646 0-65.10715-26.510696Q0 970.525392 0 932.396773z m77.972635 0q0 13.489266 14.346965 13.489266h906.977694q14.346965 0 14.346965-13.567239V249.200543q0-13.567239-14.346965-13.567239H545.808447a38.986318 38.986318 0 0 1-29.785546-13.801156L394.38559 77.972635H92.3196Q77.972635 77.972635 77.972635 91.539874V932.552719z" fill="#333333"></path>
<path d="M38.986318 420.506422h1013.644259v77.972636H38.986318v-77.972636z" fill="#333333"></path>
<path d="M0 301.754099a38.986318 38.986318 0 0 1 77.972635 0v315.39931a38.986318 38.986318 0 1 1-77.972635 0V301.754099z" fill="#333333"></path>
</svg>
<span class="text-[12px]">{{ node.pageName }}</span>
</div>
<div v-else class="children pl-4 rounded-[6px] hover:bg-[#E4EFFF]" @click.stop="selectPage(node)" :style="{backgroundColor:selectId === node.uuid?'#E4EFFF':''}">
<div class="">
<div class="h-32px ml-10px flex items-center cursor-pointer">
<img
v-if="node.pageScene === 'web'"
src="./assets/img/pagelist-web-page.png"
type="icon-yemian"
class="mr-10px"
/>
<img
v-else-if="node.pageScene === 'app'"
src="./assets/img/pagelist-app-page.png"
type="icon-yemian"
class="mr-10px"
/>
<img v-else class="mr-10px" src="./public/assets/preview/page.png" alt="">
<span class="text-[12px]">{{ node.pageName }}</span>
</div>
</div>
</div>
</div>
<ul v-show="isExpanded && node.folderFlag">
<li v-for="child in node.childrenList" :key="child.id">
<tree-node :node="child" :selectId="selectId" @node-click="(event) => $emit('node-click', event)"></tree-node>
</li>
</ul>
</div>
`,
data() {
return {
isExpanded: true
}
},
inject: ['getSelectId'],
computed: {
selectId () {
return this.getSelectId()
},
},
methods: {
toggle(node) {
this.isExpanded = !this.isExpanded
this.$emit('node-click', node)
},
selectPage(node){
this.$emit('node-click', node)
}
}
}
const app = createApp({
provide () {
return {
getSelectId: () => this.pageUuid
}
},
data() {
return {
message: 'Hello Vue!',
treeData: [],
appVersion: '',
pageUuid: '',
pageType:'',
pageName: '',
previewSizeOptions: [],
previewSizeW: 1920,
previewSizeH: 1080,
previewSize: '1920x1080',
cleanup: null,
currentIndex: 0,
pageList: [],
isPreview: false,
isShowTip: false,
appName: projectName,
resizeObserver: null,
setTimeout1: null,
isHtml: true,
url: '',
childUrl: '',
iframeMessage: null,
childIframe: null, // 导航页内嵌子页面添加键盘事件
}
},
watch: {
pageType:{
handler(value){
this.$nextTick(()=>{
if (!this.appName) return
this.changePageType(value)
let iframeDoc = null
let iframeAppDoc = null
const iframeKeydownHandler=(e) => {
this.keyboardEvents(e)
}
if (value === 'app') {
if (iframeDoc) {
iframeDoc.removeEventListener('keydown', iframeKeydownHandler);
}
const iframeId = this.isHtml ? '#iframeApp' : '#vueIframeApp'
const iframeApp = document.querySelector(iframeId);
// 聚焦到iframe时键盘左右切换不生效
// iframeApp.addEventListener('load', () => {
// iframeAppDoc = iframeApp.contentDocument || iframeApp.contentWindow.document;
// iframeAppDoc.addEventListener('keydown', iframeKeydownHandler);
// if (!this.isHtml) {
// const tag = iframeAppDoc.querySelector('.ai-tag')
// tag && (tag.style && (tag.style.visibility = 'hidden') || tag.style.setAtrrbute('visibility','hidden'))
// }
// });
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
} else {
// 监听web类型iframe父元素大小变化
const element = document.querySelector('.preview-web')
this.resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect
if (this.previewSize == 'auto') {
this.previewSizeW = parseInt(width)
this.previewSizeH = parseInt(height)
}
this.changeResolution()
}
})
this.resizeObserver.observe(element)
if (iframeAppDoc) {
iframeAppDoc.removeEventListener('keydown', iframeKeydownHandler);
}
// 聚焦到iframe时键盘左右切换不生效
const iframeWeb = this.isHtml ? document.getElementById('iframe') : document.getElementById('vueIframeWeb')
iframeWeb.addEventListener('load', () => {
iframeDoc = iframeWeb.contentDocument || iframeWeb.contentWindow.document;
iframeDoc.addEventListener('keydown', iframeKeydownHandler);
});
}
})
}
},
isHtml(value){
this.$nextTick(()=>{
if (!this.appName) return
let iframeDoc = null
let iframeAppDoc = null
const iframeKeydownHandler=(e) => {
this.keyboardEvents(e)
}
if (value === 'app') {
if (iframeDoc) {
iframeDoc.removeEventListener('keydown', iframeKeydownHandler);
}
const iframeId = this.isHtml ? '#iframeApp' : '#vueIframeApp'
const iframeApp = document.querySelector(iframeId);
// 聚焦到iframe时键盘左右切换不生效
// iframeApp.addEventListener('load', () => {
// iframeAppDoc = iframeApp.contentDocument || iframeApp.contentWindow.document;
// iframeAppDoc.addEventListener('keydown', iframeKeydownHandler);
// if (!this.isHtml) {
// this.$nextTick(() => {
// const tag = iframeAppDoc.querySelector('.ai-tag')
// tag && (tag.style && (tag.style.visibility = 'hidden') || tag.style.setAtrrbute('visibility','hidden'))
// })
// }
// });
if (this.resizeObserver) {
this.resizeObserver.disconnect()
this.resizeObserver = null
}
} else {
// 监听web类型iframe父元素大小变化
const element = document.querySelector('.preview-web')
this.resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const { width, height } = entry.contentRect
if (this.previewSize == 'auto') {
this.previewSizeW = parseInt(width)
this.previewSizeH = parseInt(height)
}
this.changeResolution()
}
})
this.resizeObserver.observe(element)
if (iframeAppDoc) {
iframeAppDoc.removeEventListener('keydown', iframeKeydownHandler);
}
// 聚焦到iframe时键盘左右切换不生效
const iframeWeb = this.isHtml ? document.getElementById('iframe') : document.getElementById('vueIframeWeb')
iframeWeb.addEventListener('load', () => {
iframeDoc = iframeWeb.contentDocument || iframeWeb.contentWindow.document;
iframeDoc.addEventListener('keydown', iframeKeydownHandler);
});
}
})
},
previewSize(val) {
const option = this.previewSizeOptions.find(item => item.value === val)
this.previewSizeW = option.w
this.previewSizeH= option.h
this.changeResolution()
},
previewSizeW(val) {
if (!this.appName) return
this.changeResolution()
},
previewSizeH(val) {
if (!this.appName) return
this.changeResolution()
}
},
mounted() {
document.querySelector('#app').style.display = 'block'
this.getPageList()
this.communicationFun()
},
methods: {
communicationFun () {
this.iframeMessage = useEventListener(window, 'message', async (event) => {
// 处理消息 判断data值是页面id时处理切换页面
if (event.source != window) {
const currentPage = this.pageList.find(item => item.pageUuid == event.data)
this.pageUuid = currentPage.pageUuid
this.pageName = currentPage.pageName
this.pageType = currentPage.pageScene
this.isHtml = currentPage.codeType === 'html'
this.getAppPageVersion(currentPage)
this.currentIndex = this.pageList.findIndex(item=>item.pageUuid == this.pageUuid) || 0;
}
}, true)
},
// 修改页面类型
changePageType(value) {
const autoSize = {
label: "自定义",
value: "auto",
w: 375,
h: 812
}
if (value === 'app') {
this.previewSizeOptions = [autoSize, ...resolutionInfo.app]
this.previewSizeW = 393
this.previewSizeH= 852
this.previewSize = '393x852-16'
} else {
let iframe = document.querySelector('.preview-web')
autoSize.w = parseInt(iframe.clientWidth)
autoSize.h = parseInt(iframe.clientHeight)
this.previewSizeOptions = [autoSize, ...resolutionInfo.web]
this.previewSizeW = autoSize.w
this.previewSizeH= autoSize.h
this.previewSize = 'auto'
}
this.changeResolution()
},
// 找第一个页面
findFirstPage(treeData) {
const queue = [...treeData];
while (queue.length > 0) {
const item = queue.shift();
// 如果是有效页面,直接返回
if (item.folderFlag === false && item.uuid) {
return item;
}
// 如果是文件夹且有子节点,将子节点加入队列前端(优先处理)
if (item.folderFlag === true && item.childrenList?.length) {
queue.unshift(...item.childrenList);
}
// 如果是空文件夹,自动跳过(继续循环)
}
return null;
},
// 页面列表
getPageList() {
this.treeData = filterByHasVersion(treeArr)
// const currentPage = this.treeData.find(item => item.default) || this.treeData[0]
const currentPage = this.findFirstPage(this.treeData)
console.log(this.treeData, this.pageList);
this.pageUuid = currentPage.pageUuid
this.pageName = currentPage.pageName
this.pageType = currentPage.pageScene
this.isHtml = currentPage.codeType === 'html'
this.$nextTick(()=>{
this.getAppPageVersion(currentPage)
})
collectItems(this.treeData, this.pageList);
this.currentIndex = this.pageList.findIndex(item=>item.pageUuid == this.pageUuid) || 0;
this.cleanup = this.createKeyboardNavigation();
},
// 获取应用中页面内容
async getAppPageVersion(info) {
const isApp = this.pageType == 'app'
let iframe = this.isHtml ? document.getElementById('iframe') : document.getElementById('vueIframeWeb')
if (isApp) {
iframe = document.getElementById('iframeApp')
}
if (info.parentUrl) {
iframe.src = info.parentUrl
this.childUrl = info.url
} else {
iframe.src = info.url;
}
},
loadHtmlIframe() {
if (this.childIframe) {
this.childIframe()
this.childIframe = null
}
const isApp = this.pageType == 'app'
// let template = handleCode(this.pageHtml, !isApp);
let iframe = document.getElementById('iframe')
if (isApp) {
iframe = document.getElementById('iframeApp')
}
const htmlIframe = iframe.contentDocument || iframe.contentWindow.document;
if (htmlIframe) {
const navIframe = htmlIframe.getElementById('navigation') || htmlIframe.getElementById('contentFrame')
this.$nextTick(() => {
const top = this.checkIframePositionSimple(htmlIframe)
if (navIframe) {
if (top) {
navIframe.style.height = `calc(100vh - ${top}px)`; // 减去导航高度
}
// navIframe.srcdoc = handleCode(this.childUrl, !isApp);
navIframe.src = this.childUrl
const iframeKeydownHandler=(e) => {
this.keyboardEvents(e)
}
navIframe.addEventListener('load', () => {
const navIframeDoc = navIframe.contentDocument || navIframe.contentWindow.document;
this.childIframe = useEventListener(navIframeDoc, 'keydown', iframeKeydownHandler)
});
}
var homeMenu = htmlIframe.querySelector(`[data-uuid="${this.pageUuid}"]`)
let currentMenu = htmlIframe.querySelector('.active-menu')
if (homeMenu) {
currentMenu?.classList.remove('active-menu')
homeMenu.classList.add('active-menu');
}
})
}
},
// 获取子页面位置信息
checkIframePositionSimple(htmlIframe) {
const iframe = htmlIframe.getElementById('navigation');
if (iframe) {
const iframeRect = iframe.getBoundingClientRect();
return iframeRect.top
}
return
},
renderIframe(pageHtml) {
// const isApp = this.pageType == 'app'
// let template = handleCode(pageHtml, !isApp);
// let iframe = document.getElementById('iframe')
// if (isApp) {
// iframe = document.getElementById('iframeApp')
// }
// if(isIOSWechat()) {
// const parser = new DOMParser();
// const doc = parser.parseFromString(template, 'text/html');
// const updatedHtmlString = new XMLSerializer().serializeToString(doc);
// const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
// iframeDocument.write(updatedHtmlString);
// return
// }
// if(!this.isHtml) {
// return
// }
// iframe.srcdoc = template
},
// 点击目录切换页面
handleChangePage(node) {
if (!node.folderFlag) {
this.pageName = node.pageName
this.pageType = node.pageScene
this.$nextTick(()=>{
this.getAppPageVersion(node)
})
}
this.currentIndex = this.pageList.findIndex(item=>item.pageUuid == node.uuid) || 0;
this.pageUuid = node.uuid
},
changeResolution() {
if (this.pageType === 'app') {
const contentApp = document.querySelector('.preview-suitable-content-app')
// contentApp.style.aspectRatio = Number(this.previewSizeW)/Number(this.previewSizeH)
const borderDom = document.querySelector('.app-border')
const scale = Math.min(contentApp.offsetWidth/Number(this.previewSizeW), contentApp.offsetHeight/Number(this.previewSizeH))
borderDom.style.transform = `scale(${scale})`
} else {
if (this.appName) {
const contentApp = document.querySelector('.page-content')
let borderDom = this.isHtml ? document.getElementById('iframe') : document.getElementById('vueIframeWeb')
const scale = Math.min(contentApp.offsetWidth/Number(this.previewSizeW), contentApp.offsetHeight/Number(this.previewSizeH))
borderDom.style.transform = `scale(${scale})`
}
}
},
changeCurrentIndex(num) {
if (num == -1) {
if (this.currentIndex > 0) {
this.currentIndex--
}
} else {
if (this.currentIndex < this.pageList.length - 1) {
this.currentIndex++
}
}
this.handleChangePage(this.pageList[this.currentIndex])
},
// 键盘事件
keyboardEvents(event) {
if (event.key === 'ArrowLeft') {
// 向左键:移动到前一个项目,如果已经是第一个则不处理
event.preventDefault();
this.changeCurrentIndex(-1)
} else if (event.key === 'ArrowRight') {
// 向右键:移动到下一个项目,如果已经是最后一个则不处理
event.preventDefault();
this.changeCurrentIndex(1)
} else if (event.key === 'Escape') {
this.isPreview = false
this.isShowTip = false
clearTimeout(this.setTimeout1)
this.setTimeout1 = null
}
},
createKeyboardNavigation() {
// 处理键盘事件
const handleKeyDown = (event) => {
if (this.pageList.length === 0) return;
this.keyboardEvents(event)
};
// 添加事件监听
window.addEventListener('keydown', handleKeyDown);
// 返回一个移除事件监听的函数
return () => {
window.removeEventListener('keydown', handleKeyDown);
};
},
toPreview() {
this.isPreview = true
this.isShowTip = true
this.setTimeout1 = setTimeout(()=>{
this.isShowTip = false
}, 5000)
},
vueIfameloaded() {
this.$nextTick(() => {
const iframeId = this.pageType === 'app' ? '#vueIframeApp' : '#vueIframeWeb'
const iframeApp = document.querySelector(iframeId);
// const iframeAppDoc = iframeApp.contentDocument || iframeApp.contentWindow.document;
// const tag = iframeAppDoc.querySelector('.ai-tag')
// tag && (tag.style && (tag.style.visibility = 'hidden') || tag.style.setAtrrbute('visibility','hidden'))
})
}
},
beforeDestroy() {
this.cleanup()
if (this.resizeObserver) {
this.resizeObserver.disconnect()
}
if (this.iframeMessage) {
this.iframeMessage()
this.iframeMessage = null
}
if (this.childIframe) {
this.childIframe()
this.childIframe = null
}
}
})
app.component('treeNode', treeNode);
app.mount('#app')
// 过滤用户没有生成的页面
function filterByHasVersion(data) {
// 如果不是数组,直接返回
if (!Array.isArray(data)) return data;
// 使用 filter 移除 hasVersion === 1 的对象
return data.filter(item => {
if (item.hasVersion === '1') {
return false; // 移除该对象
}
// 递归处理 children
if (item.children && Array.isArray(item.children)) {
item.children = filterByHasVersion(item.children);
}
return true; // 保留该对象
});
}
/**
* 用于遍历嵌套数组中 folderFlag 为 false 的对象
* @param {Array} items - 要遍历的嵌套数组
*/
function collectItems(nodes, flatItems) {
nodes.forEach((node) => {
if (node.folderFlag === false) {
flatItems.push({
...node,
});
}
if (node.childrenList && node.childrenList.length > 0) {
collectItems(node.childrenList, flatItems);
}
});
}

View File

@@ -0,0 +1,214 @@
const resolutionInfo = {
"web": [
{
"label": "网页 1920",
"value": "1920x1080",
"w": 1920,
"h": 1080
},
{
"label": "网页 1440",
"value": "1440x1024",
"w": 1440,
"h": 1024
},
{
"label": "MacBook Pro 16\"",
"value": "1728x1117",
"w": 1728,
"h": 1117
},
{
"label": "MacBook Pro 14\"",
"value": "1512x982",
"w": 1512,
"h": 982
},
{
"label": "MacBook Pro",
"value": "1440x900",
"w": 1440,
"h": 900
},
{
"label": "MacBook Air 13",
"value": "1280x832",
"w": 1280,
"h": 832
},
{
"label": "iMac",
"value": "2240x1260",
"w": 2240,
"h": 1260
},
{
"label": "华为 MateBook 16s",
"value": "1260x840",
"w": 1260,
"h": 840
},
{
"label": "华为 MateBook 14s",
"value": "1260x840-14",
"w": 1260,
"h": 840
}
],
"app": [
{
"label": "iPhone 16 Pro Max",
"value": "440x956-16pm",
"w": 440,
"h": 956
},
{
"label": "iPhone 16 Pro",
"value": "402x874-16p",
"w": 402,
"h": 874
},
{
"label": "iPhone 16 Plus",
"value": "430x932-16+",
"w": 430,
"h": 932
},
{
"label": "iPhone 16",
"value": "393x852-16",
"w": 393,
"h": 852
},
{
"label": "iPhone 15 Pro Max",
"value": "430x932-15pm",
"w": 430,
"h": 932
},
{
"label": "iPhone 15 Pro",
"value": "393x852-15p",
"w": 393,
"h": 852
},
{
"label": "iPhone 15 Plus",
"value": "430x932-15+",
"w": 430,
"h": 932
},
{
"label": "iPhone 15",
"value": "393x852-15",
"w": 393,
"h": 852
},
{
"label": "iPhone 14 Plus",
"value": "428x926-14+",
"w": 428,
"h": 926
},
{
"label": "iPhone 14",
"value": "390x844-14",
"w": 390,
"h": 844
},
{
"label": "iPhone 13 mini",
"value": "375x812-13m",
"w": 375,
"h": 812
},
{
"label": "iPhone 11",
"value": "414x896-11",
"w": 414,
"h": 896
},
{
"label": "iPhone SE",
"value": "320x568-se",
"w": 320,
"h": 568
},
{
"label": "Mate 70",
"value": "405x896-m70",
"w": 405,
"h": 896
},
{
"label": "Mate 70 Pro",
"value": "438x944-m70p",
"w": 438,
"h": 944
},
{
"label": "Mate 70 Pro+",
"value": "438x944-m70p+",
"w": 438,
"h": 944
},
{
"label": "Mate 60",
"value": "405x896-m60",
"w": 405,
"h": 896
},
{
"label": "Mate 60 Pro",
"value": "420x907-m60p",
"w": 420,
"h": 907
},
{
"label": "Mate XT-单屏",
"value": "336x744-mxt-s",
"w": 336,
"h": 744
},
{
"label": "Mate XT-双屏",
"value": "682x744-mxt-d",
"w": 682,
"h": 744
},
{
"label": "Mate XT-三屏",
"value": "1061x744-mxt-t",
"w": 1061,
"h": 744
},
{
"label": "Mate X5-展开",
"value": "741x832-mx5-u",
"w": 741,
"h": 832
},
{
"label": "Mate X5-折叠",
"value": "360x835-mx5-f",
"w": 360,
"h": 835
},
{
"label": "小米 15",
"value": "400x890-mi15",
"w": 400,
"h": 890
},
{
"label": "Samsung Galaxy S23",
"value": "360x7-s23",
"w": 360,
"h": 780
}
]
}
const treeArr = [{"appuuid":"1970803702419685376","buildType":1,"codeType":"html_module","configurationInformation":"{\"freelayoutType\":\"custom\",\"minWidth\":\"\",\"maxWidth\":\"\",\"customWidth\":\"adaptive\",\"customHeight\":\"adaptive\",\"minHeight\":\"\",\"maxHeight\":\"\",\"backgroundType\":\"color\",\"background\":\"#fff\",\"grid\":\"1\",\"height\":\"\",\"width\":\"\"}","createTime":"2025-09-24 19:14:09","createUser":"WX20250924SL0K","default":false,"folderFlag":false,"id":270773,"name":"UI页面起草","pageName":"UI页面起草","pageScene":"web","pageType":"0","pageUuid":"1970808971233067008","sort":3,"status":"0","updateTime":"2025-09-24 20:25:37","updateUser":"WX20250924SL0K","url":"./assets/page/1970808971233067008.html","uuid":"1970808971233067008"}]
const projectName = '未命名'

View File

@@ -0,0 +1,152 @@
// 保存原生方法
const nativeElementQuerySelector = Element.prototype.querySelector;
const nativeDocumentQuerySelector = Document.prototype.querySelector;
function ytCustomQuerySelector(selector) {
// 第二步尝试用选择器获取DOM元素
// 执行原生选择器查询
const foundElement = this === document ?
nativeDocumentQuerySelector.call(this, selector) :
nativeElementQuerySelector.call(this, selector);
if (foundElement) {
// 设置属性
if (!foundElement.hasAttribute('data-selectorname')) {
foundElement.setAttribute('data-selectorname', selector);
}
// 第三步:直接返回找到的元素
return foundElement;
}
// 如果通过选择器没找到尝试通过data-selectorName属性查找
const allElements = document.querySelectorAll('[data-selectorname]');
for (let i = 0; i < allElements.length; i++) {
if (allElements[i].getAttribute('data-selectorname') === selector) {
return allElements[i];
}
}
// 如果都没找到返回null
return null;
}
// 如果需要也重写querySelectorAll可以类似实现
// 重写原生的querySelector
Document.prototype.querySelector = ytCustomQuerySelector
Element.prototype.querySelector = ytCustomQuerySelector
const nativeElementInsertBefore = Element.prototype.insertBefore;
function ytCustomInsertBefore(newNode, referenceNode) {
// 当前元素作为默认父元素
const defaultParentNode = this;
// 检查参考节点是否存在
if (!referenceNode) {
// 如果没有提供参考节点,直接添加到末尾
return nativeElementInsertBefore.call(defaultParentNode, newNode, null);
}
// 检查参考节点是否仍然是父节点的直接子节点
if (referenceNode.parentNode === defaultParentNode) {
// 正常情况:参考节点仍在父节点下,直接插入
return nativeElementInsertBefore.call(defaultParentNode, newNode, referenceNode);
}
// 检查参考节点是否有 data-ytparentvalue 属性(被移动出去的节点)
const referenceParentValue = referenceNode.getAttribute('data-ytparentvalue');
if (referenceParentValue) {
// 查找具有匹配 data-ytextravalue 的父元素
const actualParentNode = document.querySelector('[data-ytextravalue="' + referenceParentValue + '"]');
if (actualParentNode) {
// 获取参考节点原来的索引位置
const originalIndex = referenceNode.getAttribute('data-ytoriginindex');
if (originalIndex !== null && !isNaN(originalIndex)) {
// 获取实际父节点当前的所有子节点
const children = Array.from(actualParentNode.children);
// 查找应该插入的位置
for (let i = 0; i < children.length; i++) {
const child = children[i];
const childOriginalIndex = child.getAttribute('data-ytoriginindex');
// 如果子节点有原始索引,并且比参考节点的原始索引大
if (childOriginalIndex !== null && !isNaN(childOriginalIndex)) {
if (parseInt(childOriginalIndex) > parseInt(originalIndex)) {
// 找到第一个索引更大的节点,插入到它前面
return nativeElementInsertBefore.call(actualParentNode, newNode, child);
}
}
}
// 如果没有找到更大的索引,插入到最后
return nativeElementInsertBefore.call(actualParentNode, newNode, null);
}
// 没有原始索引信息,插入到实际父元素的最后
return nativeElementInsertBefore.call(actualParentNode, newNode, null);
}
}
// 默认情况:插入到当前父元素的最后
return nativeElementInsertBefore.call(defaultParentNode, newNode, null);
}
// 重写原生 insertBefore 方法
Element.prototype.insertBefore = ytCustomInsertBefore;
// 需要给新添加的a标签跳转链接加入一些必要的样式 保证加入后不影响原来的布局
function addUniqueStyle(cssText, id = 'custom-style') {
const targetDom = document.getElementById(id)
if (targetDom && targetDom.tagName === 'STYLE') return; // 已存在则跳过
const style = document.createElement('style');
style.id = id;
style.innerHTML = cssText;
document.head.appendChild(style);
}
addUniqueStyle('.yt-a-defalut-link[custom-a="true"] > * { margin:0;flex:1; }')
// 定义要劫持的属性
const ytCustomProperties = ['textContent', 'innerText'];
ytCustomProperties.forEach(prop => {
let descriptor = Object.getOwnPropertyDescriptor(Element.prototype, prop) ||
Object.getOwnPropertyDescriptor(Node.prototype, prop);
if (descriptor && descriptor.set && descriptor.get) {
const originalGet = descriptor.get; // 保存原生 getter
const originalSet = descriptor.set;
Object.defineProperty(Element.prototype, prop, {
get: function () {
return originalGet.call(this); // 保持原生 getter 逻辑
},
set: function (value) {
// 优先取 data-yteditvalue否则用传入的 value
const finalValue = this.dataset.yteditvalue ?? value;
originalSet.call(this, finalValue);
},
configurable: true,
});
}
});
function ytCustomLinkNavigation () {
const parseWithURLSearchParams = (queryString) => {
const params = queryString.split('&')
const result = {}
params.forEach(param => {
const key = param.split('=')[0]
const value = param.split('=')[1]
result[key] = value
})
return result
}
const topWin = window.top
const href = window.event.currentTarget.getAttribute('custom-href')
if (href) {
const newParams = parseWithURLSearchParams(href)
topWin.postMessage(newParams.uuid, '*');
}
}

View File

@@ -0,0 +1,36 @@
function handleCode (code, isWeb) {
if (isWeb) {
return code;
}
// 要插入的滚动条样式
const scrollbarStyle = `
<style>
::-webkit-scrollbar {
width: 0px;
}
</style>
`;
// 将样式插入到 <head> 标签内(如果存在 <head>
let modifiedHtml = code;
if (/\s*<\/head\s*>/i.test(modifiedHtml)) {
modifiedHtml = modifiedHtml.replace(/(\s*<\/head\s*>)/i, `${scrollbarStyle}$1`);
} else {
// 如果没有 <head>,直接插入到 <html> 之后
modifiedHtml = modifiedHtml.replace('<html>', `<html>${scrollbarStyle}`);
}
return modifiedHtml
}
const useEventListener = (element, eventType, callback, useCapture = false) => {
if (!element || !callback) return null;
const handler = (event) => callback(event);
element.addEventListener(eventType, handler, useCapture);
return () => {
element.removeEventListener(eventType, handler, useCapture);
};
};

File diff suppressed because it is too large Load Diff