Files
cms-SiteForge/integrations/art-design-pro/src/views/siteforge/VisualEditor.vue
T

36 lines
2.7 KiB
Vue
Raw Normal View History

<template>
<section class="siteforge-editor">
<el-card shadow="never">
<div class="toolbar">
<div><h2>可视化网站编辑器</h2><p>在统一管理后台内编辑,支持多列布局、图片替换、撤销/重做、设备预览和全局设计规范。</p></div>
<div class="actions"><el-select v-model="siteId" placeholder="选择网站" filterable @change="refreshPages" style="width:210px"><el-option v-for="s in sites" :key="s.id" :label="s.name" :value="s.id" /></el-select>
<el-select v-model="entryId" placeholder="选择页面" filterable style="width:220px"><el-option v-for="p in pages" :key="p.id" :label="p.title+' · '+p.route" :value="p.id" /></el-select>
<el-button type="primary" :disabled="!entryId" @click="loadEditor">打开页面</el-button></div>
</div>
</el-card>
<div class="editor-viewport" v-if="url"><iframe :key="url" :src="url" title="SiteForge Visual Studio" /></div>
<el-empty v-else description="选择网站和页面,打开可视化编辑器" />
</section>
</template>
<script setup lang="ts">
import {watch,ref,onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {preferredSite,rememberSite,sfApi} from './_shared'
interface Site{id:string;name:string}
interface Page{id:string;title:string;route:string}
const sites=ref<Site[]>([]),pages=ref<Page[]>([]),siteId=ref(''),entryId=ref(''),url=ref('')
async function get<T>(path:string):Promise<T>{return sfApi<T>(path)}
async function refreshPages(){url.value='';pages.value=[];entryId.value='';if(!siteId.value)return;try{pages.value=await get(`/api/sites/${siteId.value}/entries`);entryId.value=pages.value.find(p=>p.route==='/')?.id||pages.value[0]?.id||''}catch(e:any){ElMessage.error(e.message)}}
function loadEditor(){if(entryId.value)url.value=`/editor/${siteId.value}/${entryId.value}`}
watch(siteId,(id)=>rememberSite(id))
onMounted(async()=>{try{sites.value=await get('/api/sites');if(sites.value.length){siteId.value=preferredSite(sites.value);await refreshPages()}}catch(e:any){ElMessage.error(e.message)}})
</script>
<style scoped>
.siteforge-editor{display:flex;flex-direction:column;gap:12px;height:calc(100vh - 140px);min-height:680px}
.toolbar{display:flex;justify-content:space-between;gap:14px;align-items:center}
h2{margin:0;font-size:22px}p{margin:7px 0 0;color:var(--el-text-color-secondary);font-size:13px}.actions{display:flex;gap:10px;align-items:center}
.editor-viewport{flex:1;min-height:540px;border:1px solid var(--el-border-color);border-radius:12px;overflow:hidden;background:#fff}
.editor-viewport iframe{width:100%;height:100%;border:0}
@media(max-width:1020px){.toolbar,.actions{flex-wrap:wrap}.siteforge-editor{height:calc(100vh - 80px)}}
</style>