stdproject/frontend/src/views/system/userlogin/login_container.vue
2025-06-19 18:31:46 +08:00

228 lines
7.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import * as VueRouter from 'vue-router'
import { loadModule } from 'vue3-sfc-loader'
import * as Vue from 'vue/dist/vue.esm-bundler.js'
import { i18n } from '@/data-visualization/plugins/vue-i18n'
import * as ElementPlus from 'element-plus'
import less from 'less'
import defaultTemplate from '@/views/system/userlogin/login.vue?raw'
// import { moduleList, moduleById } from '@/api/application/module'
import { userLogin } from '@/api/data-visualization/manage/user'
import { useCache } from '@/data-visualization/hooks/web/useCache'
import { encrypt,decrypt } from '@/utils/rsaEncrypt';
import { useUserStore } from '@/store/user'
import _ from 'lodash'
import * as lodashEs from 'lodash-es'
const route = useRoute()
const router = useRouter()
const sfcCode = ref(defaultTemplate)
const previewContainer:any = ref(null)
let prevApp:any = null
// 增强的Base64转换函数
const convertToBase64 = async (imagePath:any) => {
try {
// 处理路径别名
const resolvedPath = imagePath.replace('@/', '/src/')
const response = await fetch(resolvedPath)
if (!response.ok) throw new Error(`图片加载失败: ${response.status}`)
const blob = await response.blob()
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.readAsDataURL(blob)
})
} catch (error:any) {
console.error('Base64转换失败:', error.message)
return ''
}
}
const runCode = async () => {
try {
if (prevApp) {
prevApp.unmount()
previewContainer.value.innerHTML = ''
}
const options:any = {
moduleCache: {
vue: Vue,
'element-plus': ElementPlus,
'vue/dist/vue.esm-bundler.js': Vue,
'vue-router': VueRouter,
'@/api/data-visualization/manage/user': { userLogin },
'@/data-visualization/hooks/web/useCache': { useCache },
'@/utils/rsaEncrypt':{ encrypt,decrypt },
'lodash': _,
'lodash-es': lodashEs,
'@/store/user':{ useUserStore }
},
getFile: async (fileName:any) => {
if (!fileName.startsWith('@/') && !fileName.endsWith('.less') && fileName !== 'dynamic.vue') {
try {
// 添加 ?v= 参数避免缓存问题
const module = await import(/* @vite-ignore */ `${fileName}?v=${Date.now()}`)
return {
content: `export default ${module.default || module}`,
mediaType: 'application/javascript'
}
} catch (e) {
console.error(`模块加载失败: ${fileName}`, e)
return { content: `/* 模块加载失败 */` }
}
}
if (fileName.startsWith('@/')) {
const resolvedPath = fileName.replace('@/', '/src/')
try {
const response = await fetch(resolvedPath)
return { content: await response.text() }
} catch (e) {
console.error(`文件加载失败: ${resolvedPath}`, e)
return { content: '<!-- 文件加载失败 -->' }
}
}
if (fileName === 'dynamic.vue') {
let code = sfcCode.value
// 增强的Less处理支持scoped
const lessRegex = /<style\s+.*?lang="less".*?>(.*?)<\/style>/gis
let match
while ((match = lessRegex.exec(code)) !== null) {
const [full, content] = match
try {
const { css } = await less.render(content, {
paths: ['.'], // 设置解析路径
filename: 'dynamic.less'
})
code = code.replace(full, `<style>${css}</style>`)
} catch (e) {
console.error('Less编译错误:', e.message)
code = code.replace(full, `<style>/* Less Error: ${e.message} */</style>`)
}
}
// 处理图片资源(增强路径处理)
const imgRegex = /src=['"](.+?\.(png|jpg|jpeg))['"]/gi
let imgMatch
while ((imgMatch = imgRegex.exec(code)) !== null) {
const [full, path] = imgMatch
const base64 = await convertToBase64(path)
code = code.replace(full, `src="${base64}"`)
}
return code
.replace(/<\/script>/g, '<\\/script>')
.replace(/\\\//g, '/')
}
// 处理外部Less文件支持别名
if (fileName.endsWith('.less')) {
const resolvedPath = fileName.replace('@/', '/src/')
try {
const response = await fetch(resolvedPath)
const content = await response.text()
const { css } = await less.render(content, { filename: fileName })
return { content: css, mediaType: 'text/css' }
} catch (e) {
return { content: `/* Less Error: ${e.message} */`, mediaType: 'text/css' }
}
}
return ''
},
addStyle: (css:any) => {
const style = document.createElement('style')
style.textContent = css
document.head.appendChild(style)
},
compiledCache: new Map(),
compileTemplate: true
}
const componentModule:any = await loadModule('dynamic.vue', options)
const component = componentModule.default || componentModule
const dynamicProps = ref({
id: '1927554158852841473',
name: '测试',
isExecuteEvent: false
})
// prevApp = Vue.createApp({
// render: () => Vue.h(component, dynamicProps.value)
// })
prevApp = Vue.createApp({
render: () => Vue.h(component, {
...dynamicProps.value,
router: router,
route: route
})
})
// 显式提供路由实例
prevApp.use(router)
prevApp.use(ElementPlus)
prevApp.use(i18n)
prevApp.mount(previewContainer.value)
} catch (error:any) {
console.error('运行时错误:', error)
previewContainer.value.innerHTML = `
<div class="error">
<h3>错误</h3>
<pre>${error.message}</pre>
</div>
`
}
}
onMounted(() => {
init()
runCode()
})
function init() {
// const paramss = { appId: route.query.id }
// moduleList(paramss).then(ress => {
// var arr = ress.data.data
// let list: any = {}
// arr.forEach((item: any) => {
// if (item.type == '01' && item.node_type == '02') {
// list = item
// }
// })
// if (list.id) {
// moduleById(list.id).then(res2 => {
// if (res2.data.data.canvas_style_data) {
// sfcCode.value = res2.data.data.canvas_style_data
// runCode()
// }
// })
// }else{
// sfcCode.value = defaultTemplate
// runCode()
// }
// })
}
onBeforeUnmount(() => {
});
</script>
<template>
<div class="loginBox">
<div ref="previewContainer" class="preview"></div>
</div>
</template>
<style lang="less" scoped>
.loginBox {
width: 100%;
height: 100vh;
}
.no-login{
width: 100%;
height: 100vh;
background-color: #151515;
color: #787878;
font-size: 20px;
line-height: 100vh;
text-align: center;
font-weight: 700;
}
</style>