548 lines
20 KiB
JavaScript
Executable File
548 lines
20 KiB
JavaScript
Executable File
|
||
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);
|
||
}
|
||
});
|
||
} |