feat(navigation): hard-switch to Zibll navigation and Font Awesome 5
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/**
|
||||
* Per-page Zibll header slider configuration.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Admin;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
final class PageConfigMetaBox {
|
||||
|
||||
private const META_KEY = 'page_config';
|
||||
private const NONCE_ACTION = 'aksrt_page_config_save';
|
||||
private const NONCE_NAME = 'aksrt_page_config_nonce';
|
||||
|
||||
public static function init(): void {
|
||||
add_action('add_meta_boxes_page', [self::class, 'register']);
|
||||
add_action('save_post_page', [self::class, 'save']);
|
||||
}
|
||||
|
||||
public static function register(): void {
|
||||
add_meta_box(
|
||||
'aksrt-page-config',
|
||||
__('页面配置', 'aksrt'),
|
||||
[self::class, 'render'],
|
||||
'page',
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
/** @param \WP_Post $post */
|
||||
public static function render($post): void {
|
||||
$config = get_post_meta((int) $post->ID, self::META_KEY, true);
|
||||
$config = is_array($config) ? $config : [];
|
||||
wp_nonce_field(self::NONCE_ACTION, self::NONCE_NAME);
|
||||
|
||||
$show_type = (string) ($config['header_slider_show_type'] ?? '');
|
||||
$devices = [
|
||||
'header_slider_search_s' => (array) ($config['header_slider_search_s'] ?? []),
|
||||
'header_slider_card_s' => (array) ($config['header_slider_card_s'] ?? []),
|
||||
];
|
||||
?>
|
||||
<style>
|
||||
.aksrt-page-config-grid{display:grid;grid-template-columns:220px minmax(0,1fr);gap:14px 20px;align-items:start}
|
||||
.aksrt-page-config-grid>label{font-weight:600;padding-top:7px}.aksrt-page-config-grid textarea{width:100%;font-family:Consolas,monospace}
|
||||
.aksrt-page-config-grid .description{margin:4px 0 0}.aksrt-page-config-wide{grid-column:1/-1}
|
||||
@media(max-width:782px){.aksrt-page-config-grid{grid-template-columns:1fr}.aksrt-page-config-wide{grid-column:auto}}
|
||||
</style>
|
||||
<div class="aksrt-page-config-grid">
|
||||
<label for="aksrt-header-slider-show"><?php esc_html_e('启用顶部多功能组件', 'aksrt'); ?></label>
|
||||
<div><label><input id="aksrt-header-slider-show" type="checkbox" name="page_config[header_slider_show]" value="1" <?php checked(!empty($config['header_slider_show'])); ?>> <?php esc_html_e('为当前页面使用独立配置', 'aksrt'); ?></label></div>
|
||||
|
||||
<label><?php esc_html_e('显示规则', 'aksrt'); ?></label>
|
||||
<div>
|
||||
<?php foreach (['' => __('全部显示', 'aksrt'), 'only_pc' => __('仅在 PC 端显示', 'aksrt'), 'only_sm' => __('仅在移动端显示', 'aksrt')] as $value => $label) : ?>
|
||||
<label style="margin-right:16px"><input type="radio" name="page_config[header_slider_show_type]" value="<?php echo esc_attr($value); ?>" <?php checked($show_type, $value); ?>> <?php echo esc_html($label); ?></label>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<label for="aksrt-header-slider-nav-color"><?php esc_html_e('导航栏文字颜色', 'aksrt'); ?></label>
|
||||
<div><input id="aksrt-header-slider-nav-color" type="color" name="page_config[header_slider_nav_color]" value="<?php echo esc_attr((string) ($config['header_slider_nav_color'] ?? '#ffffff')); ?>"></div>
|
||||
|
||||
<?php self::render_json_field('header_slider', __('背景内容', 'aksrt'), $config['header_slider'] ?? self::slide_defaults(), 16); ?>
|
||||
<?php self::render_json_field('header_slider_option', __('背景显示设置', 'aksrt'), $config['header_slider_option'] ?? self::slider_defaults(), 12); ?>
|
||||
|
||||
<label><?php esc_html_e('叠加搜索组件', 'aksrt'); ?></label>
|
||||
<div><?php self::render_devices('header_slider_search_s', $devices['header_slider_search_s']); ?></div>
|
||||
<?php self::render_json_field('header_slider_search_option', __('搜索组件设置', 'aksrt'), $config['header_slider_search_option'] ?? self::search_defaults(), 12); ?>
|
||||
|
||||
<label><?php esc_html_e('叠加封面卡片组件', 'aksrt'); ?></label>
|
||||
<div><?php self::render_devices('header_slider_card_s', $devices['header_slider_card_s']); ?></div>
|
||||
<?php self::render_json_field('header_slider_card_option', __('图标卡片设置', 'aksrt'), $config['header_slider_card_option'] ?? self::card_defaults(), 16); ?>
|
||||
</div>
|
||||
<p class="description"><?php esc_html_e('复杂字段使用参考主题原始数组结构的 JSON;保存后统一写入 page_config 序列化元数据,不读取任何旧字段。', 'aksrt'); ?></p>
|
||||
<?php
|
||||
}
|
||||
|
||||
/** @param mixed $value */
|
||||
private static function render_json_field(string $key, string $label, $value, int $rows): void {
|
||||
$json = wp_json_encode(is_array($value) ? $value : [], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<label for="aksrt-<?php echo esc_attr($key); ?>"><?php echo esc_html($label); ?></label>
|
||||
<div><textarea id="aksrt-<?php echo esc_attr($key); ?>" name="page_config[<?php echo esc_attr($key); ?>]" rows="<?php echo (int) $rows; ?>"><?php echo esc_textarea((string) $json); ?></textarea></div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/** @param list<string> $selected */
|
||||
private static function render_devices(string $key, array $selected): void {
|
||||
foreach (['pc_s' => __('PC 端显示', 'aksrt'), 'm_s' => __('移动端显示', 'aksrt')] as $value => $label) {
|
||||
printf(
|
||||
'<label style="margin-right:16px"><input type="checkbox" name="page_config[%1$s][]" value="%2$s" %3$s> %4$s</label>',
|
||||
esc_attr($key),
|
||||
esc_attr($value),
|
||||
checked(in_array($value, $selected, true), true, false),
|
||||
esc_html($label)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static function save(int $post_id): void {
|
||||
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
||||
return;
|
||||
}
|
||||
if (!current_user_can('edit_page', $post_id)) {
|
||||
return;
|
||||
}
|
||||
if (!isset($_POST[self::NONCE_NAME]) || !wp_verify_nonce(sanitize_text_field(wp_unslash((string) $_POST[self::NONCE_NAME])), self::NONCE_ACTION)) {
|
||||
return;
|
||||
}
|
||||
if (!isset($_POST['page_config']) || !is_array($_POST['page_config'])) {
|
||||
delete_post_meta($post_id, self::META_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
$raw = wp_unslash($_POST['page_config']);
|
||||
$config = [
|
||||
'header_slider_show' => !empty($raw['header_slider_show']),
|
||||
'header_slider_show_type' => self::enum((string) ($raw['header_slider_show_type'] ?? ''), ['', 'only_pc', 'only_sm']),
|
||||
'header_slider_nav_color' => sanitize_hex_color((string) ($raw['header_slider_nav_color'] ?? '')) ?: '#ffffff',
|
||||
'header_slider' => self::sanitize_slides(self::decode_json($raw['header_slider'] ?? '')),
|
||||
'header_slider_option' => self::sanitize_slider_options(self::decode_json($raw['header_slider_option'] ?? '')),
|
||||
'header_slider_search_s' => self::sanitize_devices($raw['header_slider_search_s'] ?? []),
|
||||
'header_slider_search_option' => self::sanitize_search_options(self::decode_json($raw['header_slider_search_option'] ?? '')),
|
||||
'header_slider_card_s' => self::sanitize_devices($raw['header_slider_card_s'] ?? []),
|
||||
'header_slider_card_option' => self::sanitize_card_options(self::decode_json($raw['header_slider_card_option'] ?? '')),
|
||||
];
|
||||
|
||||
update_post_meta($post_id, self::META_KEY, $config);
|
||||
}
|
||||
|
||||
/** @param mixed $value @return array<mixed> */
|
||||
private static function decode_json($value): array {
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
$decoded = json_decode((string) $value, true);
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
/** @param mixed $value @return list<string> */
|
||||
private static function sanitize_devices($value): array {
|
||||
return array_values(array_intersect(array_map('sanitize_key', (array) $value), ['pc_s', 'm_s']));
|
||||
}
|
||||
|
||||
/** @param list<string> $allowed */
|
||||
private static function enum(string $value, array $allowed): string {
|
||||
return in_array($value, $allowed, true) ? $value : $allowed[0];
|
||||
}
|
||||
|
||||
/** @param array<mixed> $slides @return list<array<string,mixed>> */
|
||||
private static function sanitize_slides(array $slides): array {
|
||||
$clean = [];
|
||||
foreach ($slides as $slide) {
|
||||
if (!is_array($slide)) {
|
||||
continue;
|
||||
}
|
||||
$layers = [];
|
||||
foreach ((array) ($slide['image_layer'] ?? []) as $layer) {
|
||||
if (!is_array($layer)) {
|
||||
continue;
|
||||
}
|
||||
$layers[] = [
|
||||
'image' => esc_url_raw((string) ($layer['image'] ?? '')),
|
||||
'free_size' => !empty($layer['free_size']),
|
||||
'align' => self::enum(sanitize_key((string) ($layer['align'] ?? 'center')), ['left', 'center', 'right']),
|
||||
'parallax' => max(-200, min(200, (int) ($layer['parallax'] ?? 0))),
|
||||
'parallax_scale' => max(1, min(200, (int) ($layer['parallax_scale'] ?? 100))),
|
||||
'parallax_opacity' => max(1, min(100, (int) ($layer['parallax_opacity'] ?? 100))),
|
||||
];
|
||||
}
|
||||
$text = is_array($slide['text'] ?? null) ? $slide['text'] : [];
|
||||
$link = is_array($slide['link'] ?? null) ? $slide['link'] : [];
|
||||
$clean[] = [
|
||||
'background' => esc_url_raw((string) ($slide['background'] ?? '')),
|
||||
'background_video' => esc_url_raw((string) ($slide['background_video'] ?? '')),
|
||||
'video' => esc_url_raw((string) ($slide['video'] ?? '')),
|
||||
'hide' => self::enum(sanitize_key((string) ($slide['hide'] ?? '')), ['', 'pc', 'm']),
|
||||
'link' => [
|
||||
'url' => esc_url_raw((string) ($link['url'] ?? '')),
|
||||
'target' => !empty($link['target']) ? '_blank' : '',
|
||||
'text' => sanitize_text_field((string) ($link['text'] ?? '')),
|
||||
],
|
||||
'image_layer' => $layers,
|
||||
'text' => [
|
||||
'title' => wp_kses_post((string) ($text['title'] ?? '')),
|
||||
'desc' => wp_kses_post((string) ($text['desc'] ?? '')),
|
||||
'text_align' => sanitize_html_class((string) ($text['text_align'] ?? 'left-bottom')),
|
||||
'text_size_pc' => max(12, min(50, (int) ($text['text_size_pc'] ?? 30))),
|
||||
'text_size_m' => max(12, min(50, (int) ($text['text_size_m'] ?? 20))),
|
||||
'parallax' => max(-200, min(200, (int) ($text['parallax'] ?? 40))),
|
||||
],
|
||||
];
|
||||
}
|
||||
return $clean;
|
||||
}
|
||||
|
||||
/** @param array<mixed> $value @return array<string,mixed> */
|
||||
private static function sanitize_slider_options(array $value): array {
|
||||
$defaults = array_merge(self::slider_defaults(), ['scale' => 40, 'max_height' => 500, 'min_height' => 180]);
|
||||
$value = wp_parse_args($value, $defaults);
|
||||
return [
|
||||
'direction' => self::enum(sanitize_key((string) $value['direction']), ['horizontal', 'vertical']),
|
||||
'loop' => !empty($value['loop']),
|
||||
'button' => !empty($value['button']),
|
||||
'pagination' => !empty($value['pagination']),
|
||||
'effect' => self::enum(sanitize_key((string) $value['effect']), ['slide', 'fade', 'cube', 'coverflow', 'flip']),
|
||||
'scale_height' => !empty($value['scale_height']),
|
||||
'scale' => max(10, min(300, (int) $value['scale'])),
|
||||
'auto_height' => !empty($value['auto_height']),
|
||||
'max_height' => max(120, min(800, (int) $value['max_height'])),
|
||||
'min_height' => max(100, min(500, (int) $value['min_height'])),
|
||||
'pc_height' => max(120, min(800, (int) $value['pc_height'])),
|
||||
'm_height' => max(100, min(500, (int) $value['m_height'])),
|
||||
'spacebetween' => max(0, min(500, (int) $value['spacebetween'])),
|
||||
'speed' => max(0, min(3000, (int) $value['speed'])),
|
||||
'autoplay' => !empty($value['autoplay']),
|
||||
'interval' => max(0, min(20, (int) $value['interval'])),
|
||||
];
|
||||
}
|
||||
|
||||
/** @param array<mixed> $value @return array<string,mixed> */
|
||||
private static function sanitize_search_options(array $value): array {
|
||||
$value = wp_parse_args($value, self::search_defaults());
|
||||
return [
|
||||
'show_keywords' => !empty($value['show_keywords']),
|
||||
'popular_limit' => max(2, min(50, (int) $value['popular_limit'])),
|
||||
'show_input_cat' => !empty($value['show_input_cat']),
|
||||
'in_cat' => absint($value['in_cat']),
|
||||
'more_cats' => array_values(array_filter(array_map('absint', (array) $value['more_cats']))),
|
||||
'show_type' => !empty($value['show_type']),
|
||||
'in_type' => sanitize_key((string) $value['in_type']),
|
||||
'before_html' => wp_kses_post((string) $value['before_html']),
|
||||
'after_html' => wp_kses_post((string) $value['after_html']),
|
||||
];
|
||||
}
|
||||
|
||||
/** @param array<mixed> $value @return array<string,mixed> */
|
||||
private static function sanitize_card_options(array $value): array {
|
||||
$value = wp_parse_args($value, self::card_defaults());
|
||||
$cards = [];
|
||||
foreach ((array) $value['cards'] as $card) {
|
||||
if (!is_array($card)) {
|
||||
continue;
|
||||
}
|
||||
$link = is_array($card['link'] ?? null) ? $card['link'] : [];
|
||||
$cards[] = [
|
||||
'title' => wp_kses_post((string) ($card['title'] ?? '')),
|
||||
'desc' => wp_kses_post((string) ($card['desc'] ?? '')),
|
||||
'icon' => sanitize_text_field((string) ($card['icon'] ?? '')),
|
||||
'customize_icon' => wp_kses_post((string) ($card['customize_icon'] ?? '')),
|
||||
'icon_class' => sanitize_text_field((string) ($card['icon_class'] ?? '')),
|
||||
'link' => [
|
||||
'url' => esc_url_raw((string) ($link['url'] ?? '')),
|
||||
'target' => !empty($link['target']) ? '_blank' : '',
|
||||
'text' => sanitize_text_field((string) ($link['text'] ?? '')),
|
||||
],
|
||||
];
|
||||
}
|
||||
return ['show_widget_bg' => !empty($value['show_widget_bg']), 'icon_radius4' => !empty($value['icon_radius4']), 'cards' => $cards];
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function slider_defaults(): array {
|
||||
return ['direction' => 'horizontal', 'loop' => true, 'button' => true, 'pagination' => true, 'effect' => 'slide', 'scale_height' => false, 'auto_height' => false, 'pc_height' => 550, 'm_height' => 280, 'spacebetween' => 0, 'speed' => 0, 'autoplay' => true, 'interval' => 6];
|
||||
}
|
||||
|
||||
/** @return list<array<string,mixed>> */
|
||||
private static function slide_defaults(): array {
|
||||
$base = defined('AKSRT_URI') ? trailingslashit(AKSRT_URI) . 'assets/vendor/zibll/img/' : '';
|
||||
$slides = [];
|
||||
foreach (['slider-bg.jpg', 'user_t.jpg'] as $image) {
|
||||
$slides[] = [
|
||||
'background' => $base . $image,
|
||||
'link' => ['url' => 'https://www.zibll.com/', 'target' => '_blank'],
|
||||
'text' => ['desc' => '', 'title' => ''],
|
||||
'text_align' => 'left-bottom', 'text_parallax' => 30,
|
||||
'text_size_m' => 20, 'text_size_pc' => 30,
|
||||
];
|
||||
}
|
||||
return $slides;
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function search_defaults(): array {
|
||||
return ['show_keywords' => true, 'popular_limit' => 6, 'show_input_cat' => false, 'in_cat' => 0, 'more_cats' => [], 'show_type' => false, 'in_type' => 'post', 'before_html' => '<div class="em16 font-bold mb10">更优雅的Wordpress主题</div><div>这是导航栏幻灯片多功能组件,可在后台进行配置</div>', 'after_html' => ''];
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private static function card_defaults(): array {
|
||||
return ['show_widget_bg' => false, 'icon_radius4' => false, 'cards' => [
|
||||
['icon' => 'zibsvg-vip_1', 'customize_icon' => '', 'icon_class' => 'c-yellow', 'link' => [], 'title' => '图标卡片', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'zibsvg-vip_2', 'customize_icon' => '', 'icon_class' => 'c-blue-2', 'link' => [], 'title' => '原创作品', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'zibsvg-hot', 'customize_icon' => '', 'icon_class' => 'jb-pink', 'link' => [], 'title' => '灵感来源<badge class="ml6 jb-yellow">NEW</badge>', 'desc' => '这是一个图标卡片示例'],
|
||||
['icon' => 'fa fas fa-ioxhost', 'customize_icon' => '', 'icon_class' => 'jb-cyan', 'link' => [], 'title' => '系统工具 <badge class="ml6 jb-blue">GO <span class="fa fas fa-angle-right em12"></span></badge>', 'desc' => '这是一个图标卡片示例'],
|
||||
]];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user