写作增强:翻译服务商选了谁才显示谁对应的账号输入框 #26
之前四个密钥框(百度 APPID/密钥、有道应用 ID/密钥)无论选哪家服务商 都全摆在设置页上,四组输入框里永远有三组用不上。 改动: - inc/theme-options.php:新增 $yang_prov_class() 闭包,统一生成字段 的 class。服务端就按「当前已保存的服务商」把 yq-cond-hide 打好, 首帧不闪。分组标记 yang-prov / yang-prov-<key> 写在每个字段的 'class' 上,是**唯一的分组声明处**,JS 不再抄服务商清单。 原本一个 shared 的 info 块拆成三块,每家一句自己的说明。 - inc/options-framework/js/options-custom.js:重写 yq_toggle_writing_slug_fields()。两层条件(总开关 + 服务商归属) 必须在同一函数内合成一个结果再 toggleClass —— toggleClass 的 force 是绝对赋值,拆成两个函数各 toggle 一次的话结果只取决于 注册顺序。change 事件同时监听 #w_on_slug 与 #w_provider。 - style.css:3.2.57 -> 3.2.58 两处刻意避开的老坑: - 用 class + display:none !important,不用 jQuery show()/hide(), 后者会把行内 display 写成 block,回退 3.2.28 的 flow-root 修复。 - 隐藏不 disabled 输入框,密钥字段是「留空=不修改」语义,disabled 不随表单提交,会把已保存的密钥冲掉。
This commit is contained in:
@@ -135,25 +135,49 @@ jQuery(document).ready(function ($) {
|
|||||||
// 硬编码的 jQuery)。所以主题里其它 19 处 'requires' 其实都是死代码,
|
// 硬编码的 jQuery)。所以主题里其它 19 处 'requires' 其实都是死代码,
|
||||||
// 这里不再往那个坑里加,直接写显式逻辑。
|
// 这里不再往那个坑里加,直接写显式逻辑。
|
||||||
//
|
//
|
||||||
// 为什么连说明文字一起藏:用户没勾别名生成时,那段「去百度申请 APPID」
|
// ── 3.2.58 起这里变成**两层**条件 ──────────────────────────────────
|
||||||
// 只会是噪声。
|
|
||||||
//
|
//
|
||||||
// ⚠️ 用 delegation + toggleClass('yq-cond-hide'),不直接绑 input.hide():
|
// 第一层(原有):w_on_slug 勾了 → 这一整块才出现
|
||||||
// 原因与上面的自动目录一致(.section 的 display 是 flow-root,
|
// 第二层(本次):w_provider 选了谁 → 只出现那一家的输入框
|
||||||
// jQuery 会把行内 display 写成 block,回退 3.2.28 的布局修复)。
|
//
|
||||||
// info 那块没有 .section 类,靠 CSS 里补的 .section-info 规则命中。
|
// 两层是「与」的关系:没勾别名生成时,整块都不该出现(不管下拉框选的谁)。
|
||||||
|
//
|
||||||
|
// 🔴🔴 最容易踩的坑:这两层必须**在同一个函数里一起算**,不能各写一个
|
||||||
|
// yq_toggle_xxx() 分别去 toggleClass 同一批元素。因为 toggleClass 的
|
||||||
|
// 第二参数是「绝对赋值」不是「与」:
|
||||||
|
// 函数A 按第一层算出 true → 去掉 yq-cond-hide(显示)
|
||||||
|
// 函数B 按第二层算出 false → 加上 yq-cond-hide(隐藏)
|
||||||
|
// 两个都跑完之后,结果**只取决于谁后注册**,跟真实业务无关。
|
||||||
|
// 表现就是「勾了别名生成、选了百度,百度框却时有时无」这种鬼故事。
|
||||||
|
//
|
||||||
|
// ── 分组是数据驱动的,不是 JS 里写死映射表 ────────────────────────
|
||||||
|
// 服务商清单只有 theme-writing.php 的 yang_writing_providers() 一份。
|
||||||
|
// 若在这里抄一遍 {baidu:[...], youdao:[...]},加一家要改两处,且不一致时
|
||||||
|
// **不报错**,只表现成「新服务商的框永远不出现」。所以分组标记写在 PHP
|
||||||
|
// 每个字段的 'class' 上:
|
||||||
|
// yang-prov → 我参与「按服务商显示」
|
||||||
|
// yang-prov-<key> → 我属于 <key> 这家
|
||||||
|
// 这里只读 class、和下拉框当前 value 比对,零服务商知识。
|
||||||
function yq_toggle_writing_slug_fields () {
|
function yq_toggle_writing_slug_fields () {
|
||||||
var on = jQuery('#w_on_slug').is(':checked')
|
var on = jQuery('#w_on_slug').is(':checked')
|
||||||
|
|
||||||
|
// 整块的两个「共同成员」(不区分服务商的部分)
|
||||||
jQuery('#section-w_provider').toggleClass('yq-cond-hide', !on)
|
jQuery('#section-w_provider').toggleClass('yq-cond-hide', !on)
|
||||||
jQuery('#section-w_baidu_appid').toggleClass('yq-cond-hide', !on)
|
|
||||||
jQuery('#section-w_baidu_key').toggleClass('yq-cond-hide', !on)
|
|
||||||
jQuery('#section-w_youdao_appid').toggleClass('yq-cond-hide', !on)
|
|
||||||
jQuery('#section-w_youdao_key').toggleClass('yq-cond-hide', !on)
|
|
||||||
jQuery('#section-w_timeout').toggleClass('yq-cond-hide', !on)
|
jQuery('#section-w_timeout').toggleClass('yq-cond-hide', !on)
|
||||||
jQuery('#w_provider_help').toggleClass('yq-cond-hide', !on)
|
|
||||||
|
// 按服务商分组的字段:两层条件合成一个结果再赋值
|
||||||
|
var provider = String(jQuery('#w_provider').val() || '')
|
||||||
|
|
||||||
|
jQuery('.yang-prov').each(function () {
|
||||||
|
var el = jQuery(this)
|
||||||
|
var belongs = el.hasClass('yang-prov-' + provider)
|
||||||
|
// 第一层不成立时,第二层算什么都不重要 —— 一律隐藏
|
||||||
|
el.toggleClass('yq-cond-hide', !(on && belongs))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
jQuery(document).on('change', '#w_on_slug', yq_toggle_writing_slug_fields)
|
// 两个控件任一变化都要重算:下拉框换了服务商、或者勾掉别名生成
|
||||||
|
jQuery(document).on('change', '#w_on_slug, #w_provider', yq_toggle_writing_slug_fields)
|
||||||
yq_toggle_writing_slug_fields()
|
yq_toggle_writing_slug_fields()
|
||||||
|
|
||||||
// 写作增强:「每篇最多新增几个标签」只在「自动生成标签」勾选时才有意义。
|
// 写作增强:「每篇最多新增几个标签」只在「自动生成标签」勾选时才有意义。
|
||||||
|
|||||||
+104
-21
@@ -1364,42 +1364,111 @@ function kratos_options()
|
|||||||
$prov_sel[$k] = $v['label'];
|
$prov_sel[$k] = $v['label'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── 首帧显隐:在服务端就把它算对 ──────────────────────────
|
||||||
|
*
|
||||||
|
* 不这么做的后果:进设置页时四个密钥框**全部先显示**,等
|
||||||
|
* options-custom.js 跑到 yq_toggle_writing_slug_fields() 才被藏起来 ——
|
||||||
|
* 每次进页面、每次保存后重载,都闪一下。
|
||||||
|
*
|
||||||
|
* inc/theme-avatar.php 那边踩过同一个坑,结论一样:
|
||||||
|
* 「首帧的显隐由 PHP 打 class 上(不闪),JS 只管‘点了之后’。」
|
||||||
|
*
|
||||||
|
* ⚠️ 这里读的是**当前已保存值**,不是 std。用户没保存过时
|
||||||
|
* kratos_option 会给回传的默认值,正好也是表单上显示的值,一致。 */
|
||||||
|
$yang_prov_now = (string) kratos_option('w_provider', 'mymemory');
|
||||||
|
if (!isset($prov_sel[$yang_prov_now])) {
|
||||||
|
/* 库里的值不在清单里(配置被改坏 / 服务商下架)时,按第一项算,
|
||||||
|
与 select 实际会选中的那项保持一致 —— 否则首帧和 JS 修正后不一样。
|
||||||
|
|
||||||
|
🔴 别用 key($prov_sel) 取「第一项」:那读的是数组**内部指针**,
|
||||||
|
而指针位置取决于前面有没有跑过 foreach / reset,属于隐式状态。
|
||||||
|
PHP 7 起 foreach 按值遍历不动指针,所以现在恰好对;但这是巧合,
|
||||||
|
不是保证 —— 中间插一句 reset() 或改成按引用遍历就会变。
|
||||||
|
线上硬基线是 PHP 7.4,用 array_key_first()(7.3+)才是显式的。 */
|
||||||
|
$yang_first = function_exists('array_key_first') ? array_key_first($prov_sel) : null;
|
||||||
|
if ($yang_first === null && !empty($prov_sel)) {
|
||||||
|
$yang_keys = array_keys($prov_sel);
|
||||||
|
$yang_first = $yang_keys[0];
|
||||||
|
}
|
||||||
|
$yang_prov_now = (string) $yang_first;
|
||||||
|
}
|
||||||
|
$yang_on_slug = function_exists('yang_writing_on') ? yang_writing_on('w_on_slug') : false;
|
||||||
|
|
||||||
|
/* 生成某个字段的 class。$key 为 null 表示「不按服务商分组、只看总开关」。 */
|
||||||
|
$yang_prov_class = function ($key = null) use ($yang_on_slug, $yang_prov_now) {
|
||||||
|
if ($key === null) {
|
||||||
|
return $yang_on_slug ? '' : 'yq-cond-hide';
|
||||||
|
}
|
||||||
|
$cls = 'yang-prov yang-prov-' . $key;
|
||||||
|
if (!$yang_on_slug || $key !== $yang_prov_now) {
|
||||||
|
$cls .= ' yq-cond-hide';
|
||||||
|
}
|
||||||
|
return $cls;
|
||||||
|
};
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('翻译服务商', 'kratos'),
|
'name' => __('翻译服务商', 'kratos'),
|
||||||
'desc' => __('别名生成用的翻译服务。<b>MyMemory 无需密钥,开箱可用</b>,'
|
'desc' => __('别名生成用的翻译服务。<b>MyMemory 无需密钥,开箱可用</b>,'
|
||||||
. '但它是国外服务、有每日字符限额;被限流时换成百度即可。', 'kratos'),
|
. '但它是国外服务、有每日字符限额;被限流时换成百度即可。'
|
||||||
|
. '<br>选定之后,<b>下方只出现这一家的账号输入框</b> —— 用不上的不会摆在那里碍事。', 'kratos'),
|
||||||
'std' => 'mymemory',
|
'std' => 'mymemory',
|
||||||
'id' => 'w_provider',
|
'id' => 'w_provider',
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => $prov_sel,
|
'options' => $prov_sel,
|
||||||
|
'class' => $yang_prov_class(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* ── 按服务商分组的账号字段 ────────────────────────────────
|
||||||
|
*
|
||||||
|
* 需求(3.2.58):选了谁才显示谁的输入框,别把四家的密钥框全摆着。
|
||||||
|
*
|
||||||
|
* 实现要点 —— `class` 里那两个勾子不是装饰,是**唯一的分组声明处**:
|
||||||
|
* yang-prov 「我说哪家就显示哪家」的成员标记
|
||||||
|
* yang-prov-<key> 属于哪一家,key 必须与 yang_writing_providers() 一致
|
||||||
|
*
|
||||||
|
* 为什么把分组写在 PHP 而不是 JS 里写死一张映射表:
|
||||||
|
* 服务商清单本来就只有 theme-writing.php 一份(yang_writing_providers())。
|
||||||
|
* 若 JS 里再抄一份 {baidu:[...], youdao:[...]},将来加一家就得改两处,
|
||||||
|
* 而且两边不一致时**不会报错**,只会表现成「新服务商的框永远不出现」。
|
||||||
|
* 现在 JS 只做一件事:读元素上的 class,和下拉框当前值比对。
|
||||||
|
*
|
||||||
|
* ⚠️ 框架确实会把这里的 class 拼到包裹 div 上(见
|
||||||
|
* class-options-interface.php:70 的 `$class .= ' ' . $value['class']`),
|
||||||
|
* 所以 text/password 会得到 `section section-text yang-prov ...`。
|
||||||
|
*
|
||||||
|
* ⚠️ info 类型走的是另一条分支(:272),它拼出来的 class 是
|
||||||
|
* `section section-info yang-prov ...` —— 注意它**也有 .section 类**。
|
||||||
|
* 本文件下面那段 CSS 注释里写的「info 不生成 .section 包裹层」只对
|
||||||
|
* `id="section-xxx"` 这个 id 成立,class 是有的。别再被那句话误导。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* MyMemory:无需任何账号,说明只有一句。 */
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('服务商账号填写说明', 'kratos'),
|
'name' => __('服务商账号填写说明', 'kratos'),
|
||||||
/* 给 info 一个 id:它不生成 section- 包裹层(框架对 info 特殊处理),
|
'id' => 'w_provider_help_mymemory',
|
||||||
所以只能靠这个 id 让 JS 能把它一起折叠。 */
|
'desc' => __('<b>MyMemory 不用填任何东西</b>,选它就已经可用。'
|
||||||
'id' => 'w_provider_help',
|
. '<br>它是免费公共服务,每日有字符限额;某天开始报错、别名生成不出来,'
|
||||||
'desc' => __('<b>MyMemory</b>:不用填任何东西。<br>'
|
. '多半是限额用完了 —— 换百度即可。', 'kratos'),
|
||||||
. '<b>百度翻译</b>:访问 <a href="https://fanyi-api.baidu.com/" target="_blank" rel="noopener">fanyi-api.baidu.com</a> '
|
|
||||||
. '→ 注册登录 → 进入「管理控制台」→ 在「开发者信息」里可看到 <b>APPID</b> 与 <b>密钥</b>。'
|
|
||||||
. '免费版每月 5 万字符。<br>'
|
|
||||||
. '<b>有道翻译</b>:访问 <a href="https://ai.youdao.com/" target="_blank" rel="noopener">ai.youdao.com</a> '
|
|
||||||
. '→ 注册登录 → 「创建应用」选「文本翻译」→ 在「应用管理」里可看到 <b>应用 ID</b> 与 <b>应用密钥</b>。', 'kratos'),
|
|
||||||
'type' => 'info',
|
'type' => 'info',
|
||||||
|
'class' => $yang_prov_class('mymemory'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$options[] = array(
|
||||||
|
'name' => __('百度翻译 · 账号怎么拿', 'kratos'),
|
||||||
|
'id' => 'w_provider_help_baidu',
|
||||||
|
'desc' => __('访问 <a href="https://fanyi-api.baidu.com/" target="_blank" rel="noopener">fanyi-api.baidu.com</a> '
|
||||||
|
. '→ 注册登录 → 进入「管理控制台」→ 在「开发者信息」里可看到 <b>APPID</b> 与 <b>密钥</b>。'
|
||||||
|
. '<br>免费版每月 5 万字符,对个人博客够用。', 'kratos'),
|
||||||
|
'type' => 'info',
|
||||||
|
'class' => $yang_prov_class('baidu'),
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 密钥字段用 password 类型。
|
|
||||||
*
|
|
||||||
* ⚠️ 注意:框架会把真实值写进 HTML 的 value 属性(浏览器里 F12
|
|
||||||
* 就能看到)。SMTP 密码字段现在也是这样。这里保持与之一致,
|
|
||||||
* 不单独另做一套脱敏 —— 否则同一个页面上两种行为,更容易混淆。
|
|
||||||
* 想彻底避免,只能改成「不回显、留空即不修改」,那会牵动整个
|
|
||||||
* 框架的保存语义,不在本次范围。 */
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('百度 APPID', 'kratos'),
|
'name' => __('百度 APPID', 'kratos'),
|
||||||
'desc' => __('选「百度翻译」时填写。', 'kratos'),
|
'desc' => __('在百度控制台的「开发者信息」里,形如 <code>20240xxx</code> 的一串数字。', 'kratos'),
|
||||||
'id' => 'w_baidu_appid',
|
'id' => 'w_baidu_appid',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'class' => $yang_prov_class('baidu'),
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 密钥字段的特殊处理(只对这两项生效)。
|
/* 密钥字段的特殊处理(只对这两项生效)。
|
||||||
@@ -1417,29 +1486,42 @@ function kratos_options()
|
|||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('百度密钥', 'kratos'),
|
'name' => __('百度密钥', 'kratos'),
|
||||||
'desc' => __('选「百度翻译」时填写。<b>留空表示不修改</b>已保存的密钥。', 'kratos')
|
'desc' => __('<b>留空表示不修改</b>已保存的密钥。', 'kratos')
|
||||||
. ($yang_baidu_hint !== '' ? '<br>当前已保存:<code>' . esc_html($yang_baidu_hint) . '</code>' : ''),
|
. ($yang_baidu_hint !== '' ? '<br>当前已保存:<code>' . esc_html($yang_baidu_hint) . '</code>' : ''),
|
||||||
'id' => 'w_baidu_key',
|
'id' => 'w_baidu_key',
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'std' => '',
|
'std' => '',
|
||||||
|
'class' => $yang_prov_class('baidu'),
|
||||||
|
);
|
||||||
|
|
||||||
|
$options[] = array(
|
||||||
|
'name' => __('有道翻译 · 账号怎么拿', 'kratos'),
|
||||||
|
'id' => 'w_provider_help_youdao',
|
||||||
|
'desc' => __('访问 <a href="https://ai.youdao.com/" target="_blank" rel="noopener">ai.youdao.com</a> '
|
||||||
|
. '→ 注册登录 → 「创建应用」选「文本翻译」→ 在「应用管理」里可看到 <b>应用 ID</b> 与 <b>应用密钥</b>。', 'kratos'),
|
||||||
|
'type' => 'info',
|
||||||
|
'class' => $yang_prov_class('youdao'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('有道应用 ID', 'kratos'),
|
'name' => __('有道应用 ID', 'kratos'),
|
||||||
'desc' => __('选「有道翻译」时填写。', 'kratos'),
|
'desc' => __('在「应用管理」里查。', 'kratos'),
|
||||||
'id' => 'w_youdao_appid',
|
'id' => 'w_youdao_appid',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'class' => $yang_prov_class('youdao'),
|
||||||
);
|
);
|
||||||
|
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('有道应用密钥', 'kratos'),
|
'name' => __('有道应用密钥', 'kratos'),
|
||||||
'desc' => __('选「有道翻译」时填写。<b>留空表示不修改</b>已保存的密钥。', 'kratos')
|
'desc' => __('<b>留空表示不修改</b>已保存的密钥。', 'kratos')
|
||||||
. ($yang_youdao_hint !== '' ? '<br>当前已保存:<code>' . esc_html($yang_youdao_hint) . '</code>' : ''),
|
. ($yang_youdao_hint !== '' ? '<br>当前已保存:<code>' . esc_html($yang_youdao_hint) . '</code>' : ''),
|
||||||
'id' => 'w_youdao_key',
|
'id' => 'w_youdao_key',
|
||||||
'type' => 'password',
|
'type' => 'password',
|
||||||
'std' => '',
|
'std' => '',
|
||||||
|
'class' => $yang_prov_class('youdao'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* 超时对所有服务商通用,所以只跟第一层(w_on_slug)走,不分服务商分组。 */
|
||||||
$options[] = array(
|
$options[] = array(
|
||||||
'name' => __('接口超时(秒)', 'kratos'),
|
'name' => __('接口超时(秒)', 'kratos'),
|
||||||
'desc' => __('调用翻译接口的最长等待时间,建议 8 秒。'
|
'desc' => __('调用翻译接口的最长等待时间,建议 8 秒。'
|
||||||
@@ -1447,6 +1529,7 @@ function kratos_options()
|
|||||||
'std' => '8',
|
'std' => '8',
|
||||||
'id' => 'w_timeout',
|
'id' => 'w_timeout',
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
|
'class' => $yang_prov_class(),
|
||||||
);
|
);
|
||||||
|
|
||||||
/* ══════════════════════════════════════════════════════════
|
/* ══════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
Theme Name: Kratos_Yang
|
Theme Name: Kratos_Yang
|
||||||
Text Domain: kratos_Yang
|
Text Domain: kratos_Yang
|
||||||
Version: 3.2.57
|
Version: 3.2.58
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
Description: 这是一个Kratos主题的分支版本,用来增加一些定制化功能。
|
||||||
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
Tags: 博客, 双栏, 响应式, 自定义背景, 自定义颜色, 自定义图标, 自定义菜单, 特色图像, 收录优化, 邮件中心, 主题选项, 小工具, 文章置顶, 无障碍友好
|
||||||
|
|||||||
Reference in New Issue
Block a user