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

35 lines
4.8 KiB
Vue
Raw Normal View History

<template>
<div class="page-content">
<el-row justify="space-between" align="middle" style="margin-bottom:22px">
<div><h1>网站主题中心</h1><p>版本固定、主题安装与内容重新装配</p></div>
<el-upload :auto-upload="false" :limit="1" :show-file-list="false" accept=".zip" :on-change="install"><el-button type="primary">安装主题 ZIP</el-button></el-upload>
</el-row>
<el-card shadow="never" style="margin-bottom:20px"><el-form inline><el-form-item label="当前网站"><el-select v-model="siteId" filterable style="width:310px" @change="loadPinned"><el-option v-for="s in sites" :key="s.id" :label="s.name" :value="s.id"/></el-select></el-form-item></el-form></el-card>
<el-row :gutter="18"><el-col v-for="theme in themes" :key="theme.id||theme.theme_id" :xs="24" :sm="12" :lg="8" style="margin-bottom:20px"><el-card shadow="never"><div class="theme-cover" :class="theme.id||theme.theme_id"><span>WEBSITE<br>DESIGN<br>SYSTEM</span></div><h3>{{theme.name}}</h3><p class="secondary">{{theme.description}}</p><el-space><el-tag>v{{theme.version}}</el-tag><el-tag v-if="pinned.theme===(theme.id||theme.theme_id)" type="success">当前使用</el-tag></el-space><div class="buttons"><el-button type="primary" @click="apply(theme)">应用版本</el-button><el-button @click="versions(theme)">版本列表</el-button></div></el-card></el-col></el-row>
<el-card shadow="never"><h3>主题自动装配</h3><p class="secondary">自动为旧内容应用当前主题预设,保留历史 URL、SEO、正文内容。</p><el-button @click="assemble('missing')">仅补齐缺失页面</el-button><el-button type="warning" plain @click="assemble('overwrite')">覆盖全部页面布局</el-button></el-card>
</div>
</template>
<script setup lang="ts">
import {ref,onMounted} from 'vue'
import {ElMessage,ElMessageBox} from 'element-plus'
import type {UploadFile} from 'element-plus'
interface Site{id:string;name:string}
interface Theme{id?:string;theme_id?:string;name:string;description:string;version:string}
const sites=ref<Site[]>([]),themes=ref<Theme[]>([]),siteId=ref(''),pinned=ref({theme:'',version:''}),csrf=ref('')
async function request(url:string,method='GET',payload?:unknown,isFile=false){
const headers=new Headers();if(method!=='GET')headers.set('X-SiteForge-CSRF',csrf.value);if(payload&&!isFile)headers.set('Content-Type','application/json')
const r=await fetch(url,{method,headers,credentials:'same-origin',body:payload?(isFile?payload as FormData:JSON.stringify(payload)):undefined})
const v=await r.json();if(!r.ok)throw new Error(v.detail||'请求失败');return v
}
async function load(){try{sites.value=await request('/api/sites');themes.value=await request('/api/factory/themes');if(!siteId.value)siteId.value=sites.value[0]?.id||'';await loadPinned()}catch(e:any){ElMessage.error(e.message)}}
async function loadPinned(){if(siteId.value)pinned.value=await request(`/api/factory/sites/${siteId.value}/theme`)}
async function apply(theme:Theme){if(!siteId.value)return;try{const id=theme.id||theme.theme_id;await request(`/api/factory/sites/${siteId.value}/theme`,'PUT',{theme:id,version:theme.version});ElMessage.success('已绑定主题版本');await loadPinned()}catch(e:any){ElMessage.error(e.message)}}
async function versions(theme:Theme){try{let list=await request(`/api/factory/themes/${theme.id||theme.theme_id}/versions`);await ElMessageBox.alert(list.map((x:any)=>`${x.version} (${x.installed_at||'内置'})`).join('\n'),'主题版本',{confirmButtonText:'关闭'})}catch(e:any){ElMessage.error(e.message)}}
async function install(file:UploadFile){try{const form=new FormData();if(!file.raw)throw Error('未选择 ZIP');form.append('file',file.raw);let r=await request('/api/factory/themes/install','POST',form,true);ElMessage.success(`已安装 ${r.name} v${r.version}`);await load()}catch(e:any){ElMessage.error(e.message)}}
async function assemble(mode:string){if(!siteId.value)return;try{if(mode==='overwrite')await ElMessageBox.confirm('将覆盖所有已有页面蓝图,确定继续?','风险确认');let r=await request(`/api/sites/${siteId.value}/apply-theme`,'POST',{mode});ElMessage.success(`已装配 ${r.updated} 个页面`)}catch(e:any){if(e!=='cancel')ElMessage.error(e.message)}}
onMounted(async()=>{try{csrf.value=(await request('/api/auth/me')).csrf;await load()}catch(e:any){ElMessage.error(e.message)}})
</script>
<style scoped>
h1{margin:0 0 6px;font-size:26px}p.secondary,.page-content>p{font-size:13px;color:var(--el-text-color-secondary);line-height:1.7}.theme-cover{height:174px;border-radius:7px;background:#e6eafa;padding:25px;margin-bottom:16px;color:#3c4591}.theme-cover span{font-size:25px;line-height:1;letter-spacing:-1px;font-weight:900}.theme-cover.automotive{background:#172940;color:#e4c69a}.theme-cover.industrial{background:#e3edf6;color:#1c6292}.theme-cover.minimal{background:#eeeae3;color:#3b3b36}.buttons{margin-top:20px}
</style>