@@ -1,316 +0,0 @@
< template >
< div class = "aksrt-page-container" >
< PageHeader title = "AI 连接配置" desc = "配置 AI 生成能力的连接方式。优先使用 WordPress AI Connectors;也可填写 OpenAI 兼容接口。SEO 生成入口在文章「独立 SEO」框,摘要/标签入口在前台投稿页。" / >
< div v-if = "loadError" class="aksrt-ai__form-card aksrt-ai__load-error" role="alert" >
< strong > 加载失败 : < / strong > { { loadError } }
< / div >
< div class = "aksrt-ai__status" >
< div class = "aksrt-ai__status-card" >
< div class = "aksrt-ai__status-icon" : class = "status.available ? 'aksrt-ai__status-icon--ok' : 'aksrt-ai__status-icon--off'" >
< i class = "fa fab fa-android" > < / i >
< / div >
< div class = "aksrt-ai__status-info" >
< div class = "aksrt-ai__status-label" > 可用状态 < / div >
< div class = "aksrt-ai__status-value" > { { status . available ? '可用' : '不可用' } } < / div >
< / div >
< / div >
< div class = "aksrt-ai__status-card" >
< div class = "aksrt-ai__status-icon" : class = "status.wp_ai_available ? 'aksrt-ai__status-icon--ok' : 'aksrt-ai__status-icon--warn'" >
< i class = "fa fas fa-plug" > < / i >
< / div >
< div class = "aksrt-ai__status-info" >
< div class = "aksrt-ai__status-label" > WP AI Client < / div >
< div class = "aksrt-ai__status-value" > { { status . wp _ai _available ? '已就绪' : '不可用' } } < / div >
< / div >
< / div >
< div class = "aksrt-ai__status-card" >
< div class = "aksrt-ai__status-icon" : class = "status.theme_available ? 'aksrt-ai__status-icon--ok' : 'aksrt-ai__status-icon--warn'" >
< i class = "fa fas fa-key" > < / i >
< / div >
< div class = "aksrt-ai__status-info" >
< div class = "aksrt-ai__status-label" > 主题 Provider < / div >
< div class = "aksrt-ai__status-value" > { { status . theme _available ? '已配置' : '未配置' } } < / div >
< / div >
< / div >
< div class = "aksrt-ai__status-card" >
< div class = "aksrt-ai__status-icon aksrt-ai__status-icon--info" >
< i class = "fa fas fa-road" > < / i >
< / div >
< div class = "aksrt-ai__status-info" >
< div class = "aksrt-ai__status-label" > 当前后端 < / div >
< div class = "aksrt-ai__status-value" > { { backendLabel } } < / div >
< / div >
< / div >
< / div >
< div class = "aksrt-ai__form-card" >
< h3 > 主题 Provider 配置 ( 回退链路 ) < / h3 >
< p class = "aksrt-form-hint" > 已配置主题 Provider 时优先走下方接口 ; 未配置时才尝试 WordPress AI Connectors 。 NewAPI / 兼容网关请填到 < code > / v1 < / code > ( 也可只填域名 , 保存时会自动补全 ) 。 < / p >
< div class = "aksrt-form-group" >
< label >
< input type = "checkbox" v-model = "form.enabled" > 启用主题 AI Provider
< / label >
< / div >
< div class = "aksrt-form-group" >
< label > AI Provider < / label >
< select v-model = "form.provider" class="aksrt-select" >
< option value = "none" > 不使用 < / option >
< option value = "openai" > OpenAI 兼容接口 < / option >
< / select >
< / div >
< div class = "aksrt-form-group" v-if = "form.provider !== 'none'" >
< label > API Key < / label >
< input v-model = "form.api_key" type="password" class="aksrt-input" placeholder="sk-..." >
< p class = "aksrt-form-hint" > 保存后不会回显完整密钥 < / p >
< / div >
< div class = "aksrt-form-group" v-if = "form.provider === 'openai'" >
< label > API Base URL < / label >
< input v-model = "form.api_base" type="text" class="aksrt-input" placeholder="https://llm.example.com/v1" >
< p class = "aksrt-form-hint" > 示例 : https : //api.openai.com/v1 或 https://你的网关/v1(不要填到 /chat/completions) </p>
< / div >
< div class = "aksrt-form-group" v-if = "form.provider !== 'none'" >
< label > 模型 < / label >
< input v-model = "form.model" type="text" class="aksrt-input" placeholder="gpt-3.5-turbo" >
< / div >
< div class = "aksrt-form-group" v-if = "form.provider !== 'none'" >
< label > 最大 Token 数 < / label >
< input v -model .number = " form.max_tokens " type = "number" min = "100" max = "4000" class = "aksrt-input" >
< / div >
< div class = "aksrt-form-group" v-if = "form.provider !== 'none'" >
< label > 每分钟请求限制 < / label >
< input v -model .number = " form.rate_limit " type = "number" min = "1" max = "100" class = "aksrt-input" >
< / div >
< div class = "aksrt-form-actions" >
< button class = "aksrt-button aksrt-button--primary" :disabled = "saving" @click ="saveSettings" >
{{ saving ? ' 保存中... ' : ' 保存设置 ' }}
< / button >
< / div >
< / div >
< div class = "aksrt-ai__form-card" >
< h3 > 已接通能力 < / h3 >
< table class = "aksrt-table aksrt-table--sm" >
< thead >
< tr >
< th > 能力 < / th >
< th > 入口 < / th >
< th > 状态 < / th >
< / tr >
< / thead >
< tbody >
< tr v-for = "feat in features" :key="feat.key" >
< td > { { feat . name } } < / td >
< td > { { feat . scene } } < / td >
< td >
< span class = "aksrt-badge" : class = "feat.ready ? 'aksrt-badge--success' : 'aksrt-badge--warning'" >
{ { feat . ready ? '可用' : '未就绪' } }
< / span >
< / td >
< / tr >
< / tbody >
< / table >
< p class = "aksrt-form-hint" > SEO 开关与提示词请到 「 全局 → SEO 」 配置 ( seo _ai _enabled / ai _seo _ * _prompt ) 。 < / p >
< / div >
< div class = "aksrt-ai__form-card aksrt-ai__notice" >
< h3 > < i class = "fa fas fa-exclamation-triangle" > < / i > 重要说明 < / h3 >
< ul >
< li > AI 输出仅作为 < strong > 建议 < / strong > , 不会自动写库 ; 需用户点 「 更新 / 提交 」 才保存 < / li >
< li > 主题 API Key 已配置时优先使用 ; 否则回退 WordPress AI Connectors < / li >
< li > 生成接口要求登录与编辑 / 投稿权限 ; 配置接口仍需管理员 < / li >
< / ul >
< / div >
< / div >
< / template >
< script >
import { ref , reactive , computed , onMounted } from 'vue' ;
import PageHeader from '@/components/PageHeader.vue' ;
import api from '@/api' ;
export default {
name : 'ExtensionAI' ,
components : { PageHeader } ,
setup ( ) {
const status = ref ( {
enabled : false ,
provider : 'none' ,
configured : false ,
model : '' ,
api _key : '' ,
rate _limit : 10 ,
available : false ,
backend : 'none' ,
wp _ai _available : false ,
theme _available : false ,
seo _ai _enabled : false ,
write _assist : false ,
} ) ;
const form = reactive ( {
enabled : false ,
provider : 'none' ,
api _key : '' ,
api _base : 'https://api.openai.com/v1' ,
model : 'gpt-3.5-turbo' ,
max _tokens : 1000 ,
rate _limit : 10 ,
} ) ;
const saving = ref ( false ) ;
const loadError = ref ( '' ) ;
const backendLabel = computed ( ( ) => {
const map = { wp _ai : 'WP AI Client' , theme : '主题 Provider' , none : '无' } ;
return map [ status . value . backend ] || status . value . backend || '无' ;
} ) ;
const features = computed ( ( ) => {
const avail = ! ! status . value . available ;
const seoReady = avail && ! ! status . value . seo _ai _enabled ;
return [
{ key : 'seo' , name : 'AI 生成 SEO' , scene : '文章/分类「独立 SEO」框' , ready : seoReady } ,
{ key : 'summary' , name : 'AI 生成摘要' , scene : '前台投稿页' , ready : avail } ,
{ key : 'tags' , name : 'AI 建议标签' , scene : '前台投稿页' , ready : avail } ,
] ;
} ) ;
function hydrateFromStatus ( data ) {
if ( ! data || typeof data !== 'object' ) return ;
status . value = {
enabled : ! ! data . enabled ,
provider : data . provider || 'none' ,
configured : ! ! data . configured ,
model : data . model || '' ,
api _key : '' ,
rate _limit : data . rate _limit || 10 ,
available : ! ! data . available ,
backend : data . backend || 'none' ,
wp _ai _available : ! ! data . wp _ai _available ,
theme _available : ! ! data . theme _available ,
seo _ai _enabled : ! ! data . seo _ai _enabled ,
write _assist : ! ! data . write _assist ,
} ;
form . enabled = ! ! data . enabled ;
form . provider = data . provider || 'none' ;
form . model = data . model || 'gpt-3.5-turbo' ;
form . rate _limit = data . rate _limit || 10 ;
if ( data . api _base ) form . api _base = data . api _base ;
if ( typeof data . max _tokens === 'number' && data . max _tokens > 0 ) {
form . max _tokens = data . max _tokens ;
}
}
async function loadStatus ( ) {
loadError . value = '' ;
try {
const json = await api . getAiStatus ( ) ;
hydrateFromStatus ( json . data || { } ) ;
} catch ( e ) {
console . error ( 'Load AI status failed:' , e ) ;
loadError . value = e ? . message || '加载 AI 状态失败' ;
}
}
async function saveSettings ( ) {
saving . value = true ;
try {
const payload = {
enabled : form . enabled ,
provider : form . provider ,
api _base : form . api _base ,
model : form . model ,
max _tokens : form . max _tokens ,
rate _limit : form . rate _limit ,
} ;
// Only send api_key when the user entered a new one.
if ( form . api _key && form . api _key !== '***' ) {
payload . api _key = form . api _key ;
}
const json = await api . updateAiSettings ( payload ) ;
alert ( json . message || '设置已保存' ) ;
await loadStatus ( ) ;
} catch ( e ) {
console . error ( 'Save failed:' , e ) ;
alert ( e ? . message || '保存失败' ) ;
} finally {
saving . value = false ;
}
}
onMounted ( ( ) => loadStatus ( ) ) ;
return { status , form , saving , loadError , features , backendLabel , saveSettings } ;
} ,
} ;
< / script >
< style scoped >
. aksrt - ai _ _status {
display : grid ;
grid - template - columns : repeat ( auto - fit , minmax ( 200 px , 1 fr ) ) ;
gap : 16 px ;
margin - bottom : 24 px ;
}
. aksrt - ai _ _status - card {
display : flex ;
align - items : center ;
gap : 12 px ;
padding : 16 px ;
background : # fff ;
border - radius : 8 px ;
border : 1 px solid # e8e8e8 ;
}
. aksrt - ai _ _status - icon {
width : 40 px ;
height : 40 px ;
border - radius : 8 px ;
display : flex ;
align - items : center ;
justify - content : center ;
font - size : 16 px ;
}
. aksrt - ai _ _status - icon -- ok { background : # d4edda ; color : # 155724 ; }
. aksrt - ai _ _status - icon -- warn { background : # fff3cd ; color : # 856404 ; }
. aksrt - ai _ _status - icon -- off { background : # f8d7da ; color : # 721 c24 ; }
. aksrt - ai _ _status - icon -- info { background : # d1ecf1 ; color : # 0 c5460 ; }
. aksrt - ai _ _status - value { font - size : 18 px ; font - weight : 600 ; color : var ( -- aksrt - color - text ) ; }
. aksrt - ai _ _status - label { font - size : 12 px ; color : # 888 ; }
. aksrt - ai _ _form - card {
background : # fff ;
border : 1 px solid # e8e8e8 ;
border - radius : 8 px ;
padding : 24 px ;
margin - bottom : 16 px ;
}
. aksrt - ai _ _form - card h3 { margin : 0 0 16 px ; font - size : 16 px ; font - weight : 600 ; }
. aksrt - form - hint { font - size : 12 px ; color : # 888 ; margin - top : 4 px ; }
. aksrt - ai _ _load - error {
background : # fdecea ;
border - color : # f5c2c0 ;
color : # 842029 ;
margin - bottom : 16 px ;
}
. aksrt - ai _ _notice { background : # fff8e1 ; border - color : # ffe082 ; }
. aksrt - ai _ _notice h3 { color : # e65100 ; }
. aksrt - ai _ _notice ul { margin : 0 ; padding - left : 20 px ; }
. aksrt - ai _ _notice li { margin - bottom : 8 px ; font - size : 14 px ; }
. aksrt - ai _ _notice code {
background : rgba ( 0 , 0 , 0 , 0.06 ) ;
padding : 2 px 6 px ;
border - radius : 3 px ;
font - size : 13 px ;
}
. aksrt - badge { display : inline - block ; padding : 2 px 8 px ; border - radius : 999 px ; font - size : 12 px ; }
. aksrt - badge -- success { background : # d4edda ; color : # 155724 ; }
. aksrt - badge -- warning { background : # fff3cd ; color : # 856404 ; }
. aksrt - table { width : 100 % ; border - collapse : collapse ; }
. aksrt - table th , . aksrt - table td { border - bottom : 1 px solid # eee ; padding : 10 px 8 px ; text - align : left ; }
. aksrt - form - group { margin - bottom : 14 px ; }
. aksrt - input , . aksrt - select { width : 100 % ; max - width : 480 px ; padding : 8 px 10 px ; }
. aksrt - button { padding : 8 px 16 px ; border : 0 ; border - radius : 6 px ; cursor : pointer ; }
. aksrt - button -- primary { background : # 1677 ff ; color : # fff ; }
. aksrt - button : disabled { opacity : .6 ; cursor : not - allowed ; }
< / style >