2026-10-22 16:49:25 +08:00
< template >< section class = "release-center" >< h2 > 构建与发布中心 </ h2 >< p > 构建先通过迁移与质量验收 , 再提交独立审批 ; 通过后才能部署到预先配置的 HTTPS 环境 。 </ p >
< el-card shadow = "never" >< el-space >< el-select v-model = "siteId" @change="reload" style="width:240px"><el-option v-for="s in sites" :label="s.name" :value="s.id" :key="s.id" /></el-select><el-button @click="reload" :loading="loading" > 刷新任务 </ el -button ></ el-space ></ el-card >
< el-card shadow = "never" style = "margin-top:16px" >< template # header > 构建版本 </ template >< el-table :data = "builds" max -height =" 360 " >< el-table-column prop = "id" label = "版本ID" min -width =" 170 " />< el-table-column prop = "status" label = "状态" width = "110" />< el-table-column prop = "total_pages" label = "页面数" width = "90" />< el-table-column prop = "created_at" label = "生成日期" width = "180" />< el-table-column label = "操作" min -width =" 260 " >< template # default = "{row}" >< el-button link @click ="review(row.id)" > 申请审批 </ el -button >< el-button link type = "primary" @click ="deploy(row.id,'staging')" > 发布测试环境 </ el -button >< el-button link type = "danger" @click ="deploy(row.id,'production')" > 正式发布 </ el -button ></ template ></ el-table-column ></ el-table ></ el-card >
< el-card shadow = "never" style = "margin-top:16px" >< template # header > 审批请求 </ template >< el-table :data = "requests" >< el-table-column prop = "build_id" label = "构建ID" />< el-table-column prop = "state" label = "审批状态" width = "150" />< el-table-column prop = "requested_by" label = "申请人" />< el-table-column label = "操作" >< template # default = "{row}" >< el-button v-if = "row.state==='pending'" link @click="approve(row.id)" > 审核通过 </ el -button ></ template ></ el-table-column ></ el-table ></ el-card >
</ section ></ template >
< script setup lang = "ts" >
2026-10-23 12:31:03 +08:00
import { watch , onMounted , ref } from 'vue'
2026-10-22 16:49:25 +08:00
import { ElMessage , ElMessageBox } from 'element-plus'
2026-10-23 12:31:03 +08:00
import { preferredSite , rememberSite , sfApi } from './_shared'
2026-10-22 16:49:25 +08:00
interface Site { id : string ; name : string }
const sites = ref < Site [] >([]), siteId = ref ( '' ), builds = ref < any [] >([]), requests = ref < any [] >([]), loading = ref ( false ), csrf = ref ( '' )
2026-10-23 12:31:03 +08:00
async function api ( path : string , method = 'GET' , body ?: unknown ){ return sfApi < any >( path ,{ method , headers : body === undefined ? {} : { 'Content-Type' : 'application/json' }, body : body === undefined ? undefined : JSON . stringify ( body )})}
2026-10-22 16:49:25 +08:00
async function reload (){ if ( ! siteId . value ) return ; loading . value = true ; try {[ builds . value , requests . value ] = await Promise . all ([ api ( `/api/sites/ ${ siteId . value } /builds` ), api ( `/api/factory/sites/ ${ siteId . value } /release-requests` )])} catch ( e : any ){ ElMessage . error ( e . message )} finally { loading . value = false }}
async function review ( id : string ){ try { const r = await api ( `/api/factory/sites/ ${ siteId . value } /release-requests` , 'POST' ,{ build_id : id }); ElMessage . success ( '审批请求已建立:' + r . id ); await reload ()} catch ( e : any ){ ElMessage . error ( e . message )}}
async function approve ( id : string ){ try { await ElMessageBox . confirm ( '审批人不能是申请人,是否批准?' , '审核发布' ); await api ( `/api/factory/sites/ ${ siteId . value } /release-requests/ ${ id } /review` , 'POST' ,{ state : 'approved' }); ElMessage . success ( '审批完成' ); await reload ()} catch ( e : any ){ if ( e !== 'cancel' ) ElMessage . error ( e . message )}}
async function deploy ( build_id : string , target : 'staging' | 'production' ){ try { await ElMessageBox . confirm ( `将构建版本 ${ build_id } 部署到 ${ target } ,确认目标环境已配置 HTTPS? ` , '发布确认' ); await api ( `/api/production/sites/ ${ siteId . value } /deploy` , 'POST' ,{ build_id , target }); ElMessage . success ( '远程部署接收端已确认发布' )} catch ( e : any ){ if ( e !== 'cancel' ) ElMessage . error ( e . message )}}
2026-10-23 12:31:03 +08:00
watch ( siteId ,( id )=> rememberSite ( id ))
onMounted ( async ()=>{ try { csrf . value = ( await api ( '/api/auth/me' )). csrf ; sites . value = await api ( '/api/sites' ); siteId . value = preferredSite ( sites . value ); await reload ()} catch ( e : any ){ ElMessage . error ( e . message )}})
2026-10-22 16:49:25 +08:00
</ script >
< style scoped >. release - center h2 { font - size : 24 px ; margin : 0 0 8 px }. release - center p { color : var ( -- el - text - color - secondary ); margin : 0 0 25 px ; font - size : 13 px }</ style >