Merge pull request 'fix(admin): AI 状态接口使用共享 API 客户端 nonce' (#81) from worktree-fix-ai-status-403 into main
Reviewed-on: CYL/wordpress-theme-aksrt#81
This commit is contained in:
@@ -5,4 +5,8 @@ export const extensionsApiMethods = {
|
|||||||
|
|
||||||
testIpGeo(ip) { return this.request('/extensions/ip-test', { method: 'POST', body: JSON.stringify({ ip }) }) },
|
testIpGeo(ip) { return this.request('/extensions/ip-test', { method: 'POST', body: JSON.stringify({ ip }) }) },
|
||||||
testContentAudit(text) { return this.request('/extensions/audit-test', { method: 'POST', body: JSON.stringify({ text }) }) },
|
testContentAudit(text) { return this.request('/extensions/audit-test', { method: 'POST', body: JSON.stringify({ text }) }) },
|
||||||
|
|
||||||
|
/* AI 辅助(兼容页) */
|
||||||
|
getAiStatus() { return this.request('/ai/status') },
|
||||||
|
updateAiSettings(data) { return this.request('/ai/settings', { method: 'POST', data }) },
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
<div class="aksrt-page-container">
|
<div class="aksrt-page-container">
|
||||||
<PageHeader title="AI 辅助(兼容)" desc="兼容保留的 AI 内容辅助能力,包括 SEO 建议、摘要生成、标签建议等。" />
|
<PageHeader title="AI 辅助(兼容)" desc="兼容保留的 AI 内容辅助能力,包括 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">
|
||||||
<div class="aksrt-ai__status-card">
|
<div class="aksrt-ai__status-card">
|
||||||
@@ -131,8 +135,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ref, reactive, onMounted } from 'vue';
|
import { ref, reactive, onMounted } from 'vue';
|
||||||
import PageHeader from '@/components/PageHeader.vue';
|
import PageHeader from '@/components/PageHeader.vue';
|
||||||
|
import api from '@/api';
|
||||||
const API = '/wp-json/aksrt/v1/ai';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ExtensionAI',
|
name: 'ExtensionAI',
|
||||||
@@ -159,6 +162,7 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
|
const loadError = ref('');
|
||||||
|
|
||||||
const features = [
|
const features = [
|
||||||
{ key: 'seo', name: 'SEO 建议', desc: '生成标题、描述、关键词建议', scene: '文章编辑' },
|
{ key: 'seo', name: 'SEO 建议', desc: '生成标题、描述、关键词建议', scene: '文章编辑' },
|
||||||
@@ -167,22 +171,38 @@ export default {
|
|||||||
{ key: 'excerpt', name: '摘要建议', desc: '生成吸引人的列表摘要', scene: '文章编辑' },
|
{ key: 'excerpt', name: '摘要建议', desc: '生成吸引人的列表摘要', scene: '文章编辑' },
|
||||||
];
|
];
|
||||||
|
|
||||||
async function loadStatus() {
|
function hydrateFromStatus(data) {
|
||||||
try {
|
if (!data || typeof data !== 'object') return;
|
||||||
const res = await fetch(`${API}/status`, {
|
status.value = {
|
||||||
headers: { 'X-WP-Nonce': window.aksrtAdmin?.nonce || '' },
|
enabled: !!data.enabled,
|
||||||
});
|
provider: data.provider || 'none',
|
||||||
const json = await res.json();
|
configured: !!data.configured,
|
||||||
if (json.success) {
|
model: data.model || '',
|
||||||
status.value = json.data;
|
api_key: '',
|
||||||
form.enabled = json.data.enabled || false;
|
rate_limit: data.rate_limit || 10,
|
||||||
form.provider = json.data.provider || 'none';
|
available: !!data.available,
|
||||||
form.model = json.data.model || 'gpt-3.5-turbo';
|
};
|
||||||
form.rate_limit = json.data.rate_limit || 10;
|
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;
|
||||||
|
}
|
||||||
// 不回显 api_key
|
// 不回显 api_key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadStatus() {
|
||||||
|
loadError.value = '';
|
||||||
|
try {
|
||||||
|
const json = await api.getAiStatus();
|
||||||
|
hydrateFromStatus(json.data || {});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Load AI status failed:', e);
|
console.error('Load AI status failed:', e);
|
||||||
|
loadError.value = e?.message || '加载 AI 状态失败';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,27 +210,16 @@ export default {
|
|||||||
saving.value = true;
|
saving.value = true;
|
||||||
try {
|
try {
|
||||||
const payload = { ...form };
|
const payload = { ...form };
|
||||||
// 如果密钥为空且已配置,发送掩码
|
// 如果密钥为空且已配置,发送掩码(后端应忽略 *** 保留原密钥)
|
||||||
if (!payload.api_key && status.value.configured) {
|
if (!payload.api_key && status.value.configured) {
|
||||||
payload.api_key = '***';
|
payload.api_key = '***';
|
||||||
}
|
}
|
||||||
const res = await fetch(`${API}/settings`, {
|
const json = await api.updateAiSettings(payload);
|
||||||
method: 'POST',
|
alert(json.message || '设置已保存');
|
||||||
headers: {
|
await loadStatus();
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-WP-Nonce': window.aksrtAdmin?.nonce || '',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(payload),
|
|
||||||
});
|
|
||||||
const json = await res.json();
|
|
||||||
if (json.success) {
|
|
||||||
alert('设置已保存');
|
|
||||||
loadStatus();
|
|
||||||
} else {
|
|
||||||
alert(json.message || '保存失败');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Save failed:', e);
|
console.error('Save failed:', e);
|
||||||
|
alert(e?.message || '保存失败');
|
||||||
} finally {
|
} finally {
|
||||||
saving.value = false;
|
saving.value = false;
|
||||||
}
|
}
|
||||||
@@ -218,7 +227,7 @@ export default {
|
|||||||
|
|
||||||
onMounted(() => loadStatus());
|
onMounted(() => loadStatus());
|
||||||
|
|
||||||
return { status, form, saving, features, saveSettings };
|
return { status, form, saving, loadError, features, saveSettings };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -290,6 +299,12 @@ export default {
|
|||||||
color: #888;
|
color: #888;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
.aksrt-ai__load-error {
|
||||||
|
background: #fdecea;
|
||||||
|
border-color: #f5c2c0;
|
||||||
|
color: #842029;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
.aksrt-ai__notice {
|
.aksrt-ai__notice {
|
||||||
background: #fff8e1;
|
background: #fff8e1;
|
||||||
border-color: #ffe082;
|
border-color: #ffe082;
|
||||||
|
|||||||
Reference in New Issue
Block a user