feat(ai): 后台摘要生成与 SEO 设置文案补齐
独立 SEO 框增加 AI 生成摘要写入 excerpt;全局 SEO schema 补充 AI 开关说明与提示词占位。
This commit is contained in:
@@ -2,7 +2,7 @@ export const seoSchema = {
|
||||
key: 'global.seo',
|
||||
title: 'SEO 优化',
|
||||
icon: 'fas fa-search',
|
||||
description: '站点标题、描述、关键词、Sitemap 等搜索引擎优化设置。',
|
||||
description: '站点标题、描述、关键词与 AI SEO。AI 生成入口在文章「独立 SEO」框;连接配置在「扩展 → AI 连接配置」。优先使用 WordPress AI Connectors。',
|
||||
defaults: {
|
||||
seo_enabled: true,
|
||||
seo_ai_enabled: false,
|
||||
@@ -36,9 +36,22 @@ export const seoSchema = {
|
||||
title: 'SEO 基础',
|
||||
icon: 'fas fa-globe',
|
||||
fields: [
|
||||
{ key: 'seo_enabled', label: '启用核心 SEO 优化', type: 'switch', default: true },
|
||||
{ key: 'seo_ai_enabled', label: '启用 AI SEO 生成', type: 'switch', default: false },
|
||||
{ key: 'seo_connector', label: '标题连接符', type: 'input', default: '-', placeholder: '例如:- 或 |' },
|
||||
{
|
||||
key: 'seo_enabled',
|
||||
label: '启用核心 SEO 优化',
|
||||
type: 'switch',
|
||||
default: true,
|
||||
help: '关闭后主题不再输出 SEO title/description/keywords 等 head 标签。',
|
||||
},
|
||||
{
|
||||
key: 'seo_ai_enabled',
|
||||
label: '启用 AI SEO 生成',
|
||||
type: 'switch',
|
||||
default: false,
|
||||
help: '开启后文章/分类「独立 SEO」框显示「AI 生成 SEO」按钮。需先配置 AI 连接(WP Connectors 或扩展 → AI 连接配置)。',
|
||||
dependsOn: { key: 'seo_enabled', value: true },
|
||||
},
|
||||
{ key: 'seo_connector', label: '标题连接符', type: 'input', default: '-', placeholder: '例如:- 或 |', help: 'AI 生成标题时会用此连接符追加站点名。' },
|
||||
{ key: 'seo_title', label: '站点 SEO 标题', type: 'textarea', rows: 2, default: '' },
|
||||
{ key: 'seo_keywords', label: '站点 SEO 关键词', type: 'textarea', rows: 3, default: '' },
|
||||
{ key: 'seo_description', label: '站点 SEO 描述', type: 'textarea', rows: 4, default: '' },
|
||||
@@ -46,12 +59,40 @@ export const seoSchema = {
|
||||
},
|
||||
{
|
||||
key: 'ai-seo',
|
||||
title: 'AI SEO',
|
||||
title: 'AI SEO 提示词',
|
||||
icon: 'fas fa-robot',
|
||||
description: '留空则使用内置 Zibll 风格默认提示词。填写后作为 system instruction 覆盖默认。',
|
||||
fields: [
|
||||
{ key: 'ai_seo_title_prompt', label: '标题提示词', type: 'textarea', rows: 3, default: '' },
|
||||
{ key: 'ai_seo_keywords_prompt', label: '关键词提示词', type: 'textarea', rows: 3, default: '' },
|
||||
{ key: 'ai_seo_description_prompt', label: '描述提示词', type: 'textarea', rows: 3, default: '' },
|
||||
{
|
||||
key: 'ai_seo_title_prompt',
|
||||
label: '标题提示词',
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
default: '',
|
||||
dependsOn: { key: 'seo_ai_enabled', value: true },
|
||||
placeholder: '你是一名资深的网站 SEO 编辑……直接输出标题文本本身,不要任何前缀、引号或解释。',
|
||||
help: '控制 AI 如何生成 SEO 标题。留空使用主题内置默认。',
|
||||
},
|
||||
{
|
||||
key: 'ai_seo_keywords_prompt',
|
||||
label: '关键词提示词',
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
default: '',
|
||||
dependsOn: { key: 'seo_ai_enabled', value: true },
|
||||
placeholder: '你是一名资深 SEO 编辑……使用英文逗号分隔,直接输出关键词列表。',
|
||||
help: '控制 AI 如何生成 meta keywords。留空使用主题内置默认。',
|
||||
},
|
||||
{
|
||||
key: 'ai_seo_description_prompt',
|
||||
label: '描述提示词',
|
||||
type: 'textarea',
|
||||
rows: 5,
|
||||
default: '',
|
||||
dependsOn: { key: 'seo_ai_enabled', value: true },
|
||||
placeholder: '你是一名资深的中文 SEO 编辑……字数控制在 80 至 150 个汉字之间。',
|
||||
help: '控制 AI 如何生成 meta description。留空使用主题内置默认。',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
+165
-35
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* AKSRT Admin AI SEO — generate button for post/term SEO fields
|
||||
* AKSRT Admin AI SEO — SEO generate + summary → excerpt
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
@@ -12,7 +12,7 @@
|
||||
var I18N = CFG.i18n || {};
|
||||
|
||||
function notify($btn, msg, type) {
|
||||
var wrap = $btn.closest('.aksrt-seo-meta-box, td, .form-field, p') || $btn.parentNode;
|
||||
var wrap = $btn.closest('.aksrt-seo-meta-box, td, .form-field, p, .aksrt-ai-summary-row') || $btn.parentNode;
|
||||
if (!wrap) return;
|
||||
var old = wrap.querySelector('.aksrt-ai-seo-msg');
|
||||
if (old) old.remove();
|
||||
@@ -61,36 +61,174 @@
|
||||
return errors;
|
||||
}
|
||||
|
||||
function getEditorTitle() {
|
||||
var titleInput = document.getElementById('title');
|
||||
if (titleInput && titleInput.value) return titleInput.value.trim();
|
||||
// Block editor
|
||||
try {
|
||||
if (window.wp && wp.data && wp.data.select) {
|
||||
var ed = wp.data.select('core/editor');
|
||||
if (ed && ed.getEditedPostAttribute) {
|
||||
return String(ed.getEditedPostAttribute('title') || '').trim();
|
||||
}
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
return '';
|
||||
}
|
||||
|
||||
function getEditorContent() {
|
||||
var content = '';
|
||||
var ta = document.getElementById('content');
|
||||
if (ta && ta.value) content = ta.value;
|
||||
try {
|
||||
if (window.wp && wp.data && wp.data.select) {
|
||||
var ed = wp.data.select('core/editor');
|
||||
if (ed && ed.getEditedPostContent) {
|
||||
var blockContent = ed.getEditedPostContent();
|
||||
if (blockContent) content = blockContent;
|
||||
}
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
// Strip tags for length check / API
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = content || '';
|
||||
return (div.textContent || div.innerText || content || '').trim();
|
||||
}
|
||||
|
||||
function setExcerptValue(summary) {
|
||||
if (!summary) return false;
|
||||
|
||||
// Classic: #excerpt
|
||||
var classic = document.getElementById('excerpt');
|
||||
if (classic) {
|
||||
classic.value = summary;
|
||||
classic.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
classic.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Block editor excerpt attribute
|
||||
try {
|
||||
if (window.wp && wp.data && wp.data.dispatch) {
|
||||
var d = wp.data.dispatch('core/editor');
|
||||
if (d && d.editPost) {
|
||||
d.editPost({ excerpt: summary });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
|
||||
// Fallback name selector
|
||||
var named = document.querySelector('textarea[name="excerpt"]');
|
||||
if (named) {
|
||||
named.value = summary;
|
||||
named.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setLoading(btn, on, original) {
|
||||
if (on) {
|
||||
btn.classList.add('is-loading');
|
||||
btn.textContent = I18N.generating || '生成中…';
|
||||
} else {
|
||||
btn.classList.remove('is-loading');
|
||||
btn.textContent = original;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('.aksrt-ai-seo-generate');
|
||||
if (!btn) return;
|
||||
// SEO generate
|
||||
var seoBtn = e.target.closest('.aksrt-ai-seo-generate');
|
||||
if (seoBtn) {
|
||||
e.preventDefault();
|
||||
if (seoBtn.classList.contains('is-loading') || seoBtn.disabled) return;
|
||||
|
||||
var type = seoBtn.getAttribute('data-type') || 'post';
|
||||
var id = parseInt(seoBtn.getAttribute('data-id') || '0', 10);
|
||||
if (!id) {
|
||||
notify(seoBtn, I18N.needSave || '请先保存后再生成 SEO', 'error');
|
||||
return;
|
||||
}
|
||||
if (CFG.ai && CFG.ai.available === false) {
|
||||
notify(seoBtn, I18N.notAvailable || 'AI 未配置或未启用', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var seoOriginal = seoBtn.textContent;
|
||||
setLoading(seoBtn, true, seoOriginal);
|
||||
|
||||
fetch(REST + '/ai/seo-generate', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
},
|
||||
body: JSON.stringify({ type: type, id: id }),
|
||||
})
|
||||
.then(function (r) {
|
||||
return r.json().then(function (json) {
|
||||
return { ok: r.ok, json: json };
|
||||
});
|
||||
})
|
||||
.then(function (res) {
|
||||
setLoading(seoBtn, false, seoOriginal);
|
||||
if (!res.ok || !res.json || res.json.success === false) {
|
||||
var msg =
|
||||
(res.json && (res.json.message || (res.json.data && res.json.data.message))) ||
|
||||
I18N.failed ||
|
||||
'生成失败';
|
||||
notify(seoBtn, msg, 'error');
|
||||
return;
|
||||
}
|
||||
var data = res.json.data || {};
|
||||
if (type === 'term') fillTerm(data);
|
||||
else fillPost(data);
|
||||
var errors = collectErrors(data);
|
||||
if (errors.length) notify(seoBtn, errors.join(';'), 'error');
|
||||
else notify(seoBtn, I18N.success || '已填入 SEO 字段,请检查后更新保存', 'ok');
|
||||
})
|
||||
.catch(function () {
|
||||
setLoading(seoBtn, false, seoOriginal);
|
||||
notify(seoBtn, I18N.failed || '生成失败', 'error');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Summary → excerpt
|
||||
var sumBtn = e.target.closest('.aksrt-ai-summary-generate');
|
||||
if (!sumBtn) return;
|
||||
e.preventDefault();
|
||||
if (sumBtn.classList.contains('is-loading') || sumBtn.disabled) return;
|
||||
|
||||
if (btn.classList.contains('is-loading') || btn.disabled) return;
|
||||
|
||||
var type = btn.getAttribute('data-type') || 'post';
|
||||
var id = parseInt(btn.getAttribute('data-id') || '0', 10);
|
||||
if (!id) {
|
||||
notify(btn, I18N.needSave || '请先保存后再生成 SEO', 'error');
|
||||
return;
|
||||
}
|
||||
if (CFG.ai && CFG.ai.available === false) {
|
||||
notify(btn, I18N.notAvailable || 'AI 未配置或未启用', 'error');
|
||||
notify(sumBtn, I18N.notAvailable || 'AI 未配置或未启用', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var original = btn.textContent;
|
||||
btn.classList.add('is-loading');
|
||||
btn.textContent = I18N.generating || '生成中…';
|
||||
var content = getEditorContent();
|
||||
if (!content) {
|
||||
notify(sumBtn, I18N.needContent || '请先填写正文内容', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(REST + '/ai/seo-generate', {
|
||||
var sumOriginal = sumBtn.textContent;
|
||||
setLoading(sumBtn, true, sumOriginal);
|
||||
|
||||
fetch(REST + '/ai/summary-suggest', {
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-WP-Nonce': NONCE,
|
||||
},
|
||||
body: JSON.stringify({ type: type, id: id }),
|
||||
body: JSON.stringify({
|
||||
title: getEditorTitle(),
|
||||
content: content,
|
||||
max_length: 200,
|
||||
}),
|
||||
})
|
||||
.then(function (r) {
|
||||
return r.json().then(function (json) {
|
||||
@@ -98,33 +236,25 @@
|
||||
});
|
||||
})
|
||||
.then(function (res) {
|
||||
btn.classList.remove('is-loading');
|
||||
btn.textContent = original;
|
||||
|
||||
setLoading(sumBtn, false, sumOriginal);
|
||||
if (!res.ok || !res.json || res.json.success === false) {
|
||||
var msg =
|
||||
(res.json && (res.json.message || (res.json.data && res.json.data.message))) ||
|
||||
I18N.failed ||
|
||||
'生成失败';
|
||||
notify(btn, msg, 'error');
|
||||
notify(sumBtn, msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
var data = res.json.data || {};
|
||||
if (type === 'term') fillTerm(data);
|
||||
else fillPost(data);
|
||||
|
||||
var errors = collectErrors(data);
|
||||
if (errors.length) {
|
||||
notify(btn, errors.join(';'), 'error');
|
||||
} else {
|
||||
notify(btn, I18N.success || '已填入 SEO 字段,请检查后更新保存', 'ok');
|
||||
var summary = (res.json.data && res.json.data.summary) || '';
|
||||
if (!setExcerptValue(summary)) {
|
||||
notify(sumBtn, I18N.noExcerptField || '未找到摘要字段,请展开「摘要」面板', 'error');
|
||||
return;
|
||||
}
|
||||
notify(sumBtn, I18N.summarySuccess || '已填入摘要字段,请检查后更新保存', 'ok');
|
||||
})
|
||||
.catch(function () {
|
||||
btn.classList.remove('is-loading');
|
||||
btn.textContent = original;
|
||||
notify(btn, I18N.failed || '生成失败', 'error');
|
||||
setLoading(sumBtn, false, sumOriginal);
|
||||
notify(sumBtn, I18N.failed || '生成失败', 'error');
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -64,17 +64,17 @@ class AKSRT_Admin_Schema_Global {
|
||||
'option_key' => 'aksrt_settings',
|
||||
'groups' => [
|
||||
['key' => 'basic', 'title' => 'SEO基础', 'fields' => [
|
||||
['key' => 'seo_enabled', 'label' => '启用核心SEO优化', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'seo_ai_enabled', 'label' => 'AI生成SEO内容', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'seo_connector', 'label' => 'SEO连接符', 'type' => 'input', 'default' => '-'],
|
||||
['key' => 'seo_enabled', 'label' => '启用核心SEO优化', 'type' => 'switch', 'default' => true, 'help' => '关闭后主题不再输出 SEO head 标签。'],
|
||||
['key' => 'seo_ai_enabled', 'label' => 'AI生成SEO内容', 'type' => 'switch', 'default' => false, 'help' => '开启后文章/分类独立 SEO 框显示 AI 生成按钮;需配置 AI 连接(WP Connectors 或扩展 → AI 连接配置)。', 'dependsOn' => ['key' => 'seo_enabled', 'value' => true]],
|
||||
['key' => 'seo_connector', 'label' => 'SEO连接符', 'type' => 'input', 'default' => '-', 'help' => 'AI 生成标题时用于追加站点名。'],
|
||||
['key' => 'seo_title', 'label' => 'SEO标题(title)', 'type' => 'textarea', 'default' => ''],
|
||||
['key' => 'seo_keywords', 'label' => 'SEO关键字(keywords)', 'type' => 'textarea', 'default' => ''],
|
||||
['key' => 'seo_description', 'label' => 'SEO描述(description)', 'type' => 'textarea', 'default' => ''],
|
||||
]],
|
||||
['key' => 'ai-seo', 'title' => 'AI SEO', 'fields' => [
|
||||
['key' => 'ai_seo_title_prompt', 'label' => '标题提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true]],
|
||||
['key' => 'ai_seo_keywords_prompt', 'label' => '关键词提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true]],
|
||||
['key' => 'ai_seo_description_prompt', 'label' => '描述提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true]],
|
||||
['key' => 'ai-seo', 'title' => 'AI SEO 提示词', 'fields' => [
|
||||
['key' => 'ai_seo_title_prompt', 'label' => '标题提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true], 'help' => '留空使用内置 Zibll 风格默认提示词。'],
|
||||
['key' => 'ai_seo_keywords_prompt', 'label' => '关键词提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true], 'help' => '留空使用内置默认。'],
|
||||
['key' => 'ai_seo_description_prompt', 'label' => '描述提示词', 'type' => 'textarea', 'default' => '', 'dependsOn' => ['key' => 'seo_ai_enabled', 'value' => true], 'help' => '留空使用内置默认。'],
|
||||
]],
|
||||
['key' => 'baidu', 'title' => '百度SEO', 'fields' => [
|
||||
['key' => 'seo_baidu_push', 'label' => '百度链接自动提交', 'type' => 'switch', 'default' => false],
|
||||
|
||||
@@ -57,7 +57,8 @@ class AKSRT_SEO_Meta_Box {
|
||||
$keywords = (string) get_post_meta($post->ID, self::META_KEYWORDS, true);
|
||||
$description = (string) get_post_meta($post->ID, self::META_DESCRIPTION, true);
|
||||
|
||||
$ai_btn = $this->render_ai_button('post', (int) $post->ID);
|
||||
$ai_btn = $this->render_ai_button('post', (int) $post->ID);
|
||||
$summary_btn = $this->render_summary_button((int) $post->ID);
|
||||
?>
|
||||
<div class="aksrt-seo-meta-box">
|
||||
<p>
|
||||
@@ -73,6 +74,12 @@ class AKSRT_SEO_Meta_Box {
|
||||
<textarea class="widefat" rows="3" id="aksrt_seo_description" name="aksrt_seo_description"><?php echo esc_textarea($description); ?></textarea>
|
||||
<span class="description"><?php esc_html_e('建议 50–150 字。', 'aksrt'); ?><?php echo $ai_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
|
||||
</p>
|
||||
<?php if ($summary_btn !== '') : ?>
|
||||
<p class="aksrt-ai-summary-row">
|
||||
<span class="description"><?php esc_html_e('写作辅助:根据正文生成摘要并填入「摘要」字段(不自动保存)。', 'aksrt'); ?></span>
|
||||
<?php echo $summary_btn; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@@ -200,24 +207,30 @@ class AKSRT_SEO_Meta_Box {
|
||||
'ai' => [
|
||||
'available' => !empty($status['available']),
|
||||
'seo_ai_enabled' => !empty($seo['seo_ai_enabled']) && !empty($seo['seo_enabled']),
|
||||
'write_assist' => !empty($status['available']),
|
||||
],
|
||||
'i18n' => [
|
||||
'generating' => __('生成中…', 'aksrt'),
|
||||
'generate' => __('AI 生成 SEO', 'aksrt'),
|
||||
'success' => __('已填入 SEO 字段,请检查后更新保存', 'aksrt'),
|
||||
'failed' => __('生成失败', 'aksrt'),
|
||||
'needSave' => __('请先保存后再生成 SEO', 'aksrt'),
|
||||
'notAvailable' => __('AI 未配置或未启用', 'aksrt'),
|
||||
'generating' => __('生成中…', 'aksrt'),
|
||||
'generate' => __('AI 生成 SEO', 'aksrt'),
|
||||
'generateSummary' => __('AI 生成摘要', 'aksrt'),
|
||||
'success' => __('已填入 SEO 字段,请检查后更新保存', 'aksrt'),
|
||||
'summarySuccess' => __('已填入摘要字段,请检查后更新保存', 'aksrt'),
|
||||
'failed' => __('生成失败', 'aksrt'),
|
||||
'needSave' => __('请先保存后再生成 SEO', 'aksrt'),
|
||||
'needContent' => __('请先填写正文内容', 'aksrt'),
|
||||
'notAvailable' => __('AI 未配置或未启用', 'aksrt'),
|
||||
'noExcerptField' => __('未找到摘要字段,请展开「摘要」面板', 'aksrt'),
|
||||
],
|
||||
]);
|
||||
|
||||
wp_add_inline_style('wp-admin', '
|
||||
.aksrt-ai-seo-button{display:inline-block;margin-left:8px;vertical-align:middle}
|
||||
.aksrt-ai-seo-generate{cursor:pointer}
|
||||
.aksrt-ai-seo-generate.is-loading{opacity:.6;pointer-events:none}
|
||||
.aksrt-ai-seo-generate,.aksrt-ai-summary-generate{cursor:pointer}
|
||||
.aksrt-ai-seo-generate.is-loading,.aksrt-ai-summary-generate.is-loading{opacity:.6;pointer-events:none}
|
||||
.aksrt-ai-seo-msg{display:block;margin-top:6px;font-size:12px}
|
||||
.aksrt-ai-seo-msg.is-error{color:#b32d2e}
|
||||
.aksrt-ai-seo-msg.is-ok{color:#007017}
|
||||
.aksrt-ai-summary-row{margin-top:8px;padding-top:8px;border-top:1px solid #eee}
|
||||
');
|
||||
}
|
||||
|
||||
@@ -230,7 +243,7 @@ class AKSRT_SEO_Meta_Box {
|
||||
$available = class_exists('AKSRT_AI') && AKSRT_AI::get_instance()->is_available();
|
||||
$disabled = (!$available || $id <= 0) ? ' disabled' : '';
|
||||
$title = !$available
|
||||
? esc_attr__('请先配置 AI 连接(扩展 → AI 辅助 或 WP Connectors)', 'aksrt')
|
||||
? esc_attr__('请先配置 AI 连接(扩展 → AI 连接配置 或 WP Connectors)', 'aksrt')
|
||||
: ($id <= 0 ? esc_attr__('请先保存后再生成', 'aksrt') : '');
|
||||
|
||||
return sprintf(
|
||||
@@ -242,4 +255,27 @@ class AKSRT_SEO_Meta_Box {
|
||||
esc_html__('AI 生成 SEO', 'aksrt')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台「AI 生成摘要」→ 写入 WP 摘要字段(#excerpt / 块编辑器)。
|
||||
*/
|
||||
private function render_summary_button(int $post_id): string {
|
||||
$available = class_exists('AKSRT_AI') && AKSRT_AI::get_instance()->is_available();
|
||||
if (!$available) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$disabled = $post_id <= 0 ? ' disabled' : '';
|
||||
$title = $post_id <= 0
|
||||
? esc_attr__('请先保存后再生成', 'aksrt')
|
||||
: esc_attr__('根据正文生成摘要,填入摘要框', 'aksrt');
|
||||
|
||||
return sprintf(
|
||||
'<span class="aksrt-ai-seo-button"><button type="button" class="button aksrt-ai-summary-generate"%s data-post-id="%d" title="%s">%s</button></span>',
|
||||
$disabled,
|
||||
$post_id,
|
||||
$title,
|
||||
esc_html__('AI 生成摘要', 'aksrt')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user