Commencez votre voyage transfrontalier
Contactez-nous

Développement de sites web, service à la clientèle

Service clientèle

Concepteur (réalisation de prototypes Figma)

Tableau d'affichage personnalisé WordPress

Panneaux d'affichage WordPress personnalisés : vous pouvez personnaliser plusieurs panneaux d'affichage afin qu'ils tournent automatiquement et qu'ils affichent un compte à rebours, qui peut ensuite être ajouté au menu de navigation de l'arrière-plan.

add_action('init', function() {
    register_post_type('announcement_bar', [
        'labels' => [
            'name' => '公告栏',
            'add_new_item' => '添加新公告',
            'edit_item' => '编辑公告',
        ],
        'public' => false,
        'show_ui' => true,
        'menu_icon' => 'dashicons-megaphone',
        'supports' => ['title', 'page-attributes'],
    ]);
});
add_action('add_meta_boxes', function() {
    add_meta_box('announcement_fields', '公告设置', function($post) {
        $meta = get_post_meta($post->ID);
        $val = function($key, $default = '') use ($meta) {
            return isset($meta[$key][0]) ? esc_attr($meta[$key][0]) : $default;
        };
        ?>
        <p><label>链接地址:<input type="url" name="link" value="<?php echo $val('link'); ?>" style="width:100%"></label></p>
        <p><label><input type="checkbox" name="enable_timer" value="1" <?php checked($val('enable_timer'), '1'); ?>> 启用倒计时</label></p>
        <p><label>倒计时目标时间:<input type="datetime-local" name="timer_end" value="<?php echo $val('timer_end'); ?>"></label></p>
        <?php
    }, 'announcement_bar');
});
add_action('save_post', function($post_id) {
    if (get_post_type($post_id) !== 'announcement_bar') return;
    foreach (['link', 'timer_end'] as $field) {
        if (isset($_POST[$field])) update_post_meta($post_id, $field, sanitize_text_field($_POST[$field]));
    }
    update_post_meta($post_id, 'enable_timer', isset($_POST['enable_timer']) ? '1' : '0');
});
add_action('admin_menu', function() {
    add_submenu_page(
        'edit.php?post_type=announcement_bar',
        '公告栏全局设置',
        '公告栏设置',
        'manage_options',
        'announcement_bar_options',
        function() {
            if ($_POST && current_user_can('manage_options')) {
                update_option('announcement_bar_enabled', isset($_POST['enabled']) ? 1 : 0);
                update_option('announcement_bar_autoplay', isset($_POST['autoplay']) ? 1 : 0);
                update_option('announcement_bar_interval', intval($_POST['interval']));
                update_option('announcement_bar_bg', sanitize_hex_color($_POST['bg']));
                update_option('announcement_bar_text', sanitize_hex_color($_POST['text']));
                update_option('announcement_bar_timerbg', sanitize_hex_color($_POST['timerbg']));
                update_option('announcement_bar_timertext', sanitize_hex_color($_POST['timertext']));
                echo '<div class="updated"><p>设置已保存!</p></div>';
            }
            $bg = esc_attr(get_option('announcement_bar_bg', '#222222'));
            $text = esc_attr(get_option('announcement_bar_text', '#ffffff'));
            $timerbg = esc_attr(get_option('announcement_bar_timerbg', '#ff4444'));
            $timertext = esc_attr(get_option('announcement_bar_timertext', '#ffffff'));
            ?>
            <div class="wrap">
                <h2>公告栏全局设置</h2>
                <form method="post">
                    <table class="form-table">
                        <tr>
                            <th><label>启用公告栏</label></th>
                            <td><input type="checkbox" name="enabled" value="1" <?php checked(get_option('announcement_bar_enabled'), 1); ?>></td>
                        </tr>
                        <tr>
                            <th><label>启用轮播(所有设备)</label></th>
                            <td><input type="checkbox" name="autoplay" value="1" <?php checked(get_option('announcement_bar_autoplay'), 1); ?>></td>
                        </tr>
                        <tr>
                            <th><label>轮播间隔(毫秒)</label></th>
                            <td><input type="number" name="interval" value="<?php echo esc_attr(get_option('announcement_bar_interval', 5000)); ?>" min="1000" step="500"></td>
                        </tr>
                        <tr>
                            <th><label>背景颜色</label></th>
                            <td>
                                <input type="color" id="bg_color_picker" value="<?php echo $bg; ?>">
                                <input type="text" name="bg" id="bg_color_text" value="<?php echo $bg; ?>" style="width:100px;">
                            </td>
                        </tr>
                        <tr>
                            <th><label>字体颜色</label></th>
                            <td>
                                <input type="color" id="text_color_picker" value="<?php echo $text; ?>">
                                <input type="text" name="text" id="text_color_text" value="<?php echo $text; ?>" style="width:100px;">
                            </td>
                        </tr>
                        <tr>
                            <th><label>倒计时背景颜色</label></th>
                            <td>
                                <input type="color" id="timerbg_color_picker" value="<?php echo $timerbg; ?>">
                                <input type="text" name="timerbg" id="timerbg_color_text" value="<?php echo $timerbg; ?>" style="width:100px;">
                            </td>
                        </tr>
                        <tr>
                            <th><label>倒计时字体颜色</label></th>
                            <td>
                                <input type="color" id="timertext_color_picker" value="<?php echo $timertext; ?>">
                                <input type="text" name="timertext" id="timertext_color_text" value="<?php echo $timertext; ?>" style="width:100px;">
                            </td>
                        </tr>
                    </table>
                    <?php submit_button(); ?>
                </form>
            </div>
            <script>
            document.addEventListener('DOMContentLoaded',function(){
                var bgPicker=document.getElementById('bg_color_picker');
                var bgText=document.getElementById('bg_color_text');
                var textPicker=document.getElementById('text_color_picker');
                var textText=document.getElementById('text_color_text');
                var timerbgPicker=document.getElementById('timerbg_color_picker');
                var timerbgText=document.getElementById('timerbg_color_text');
                var timertextPicker=document.getElementById('timertext_color_picker');
                var timertextText=document.getElementById('timertext_color_text');
                if(bgPicker&&bgText){
                    bgPicker.addEventListener('input',function(){bgText.value=bgPicker.value;});
                    bgText.addEventListener('input',function(){bgPicker.value=bgText.value;});
                }
                if(textPicker&&textText){
                    textPicker.addEventListener('input',function(){textText.value=textPicker.value;});
                    textText.addEventListener('input',function(){textPicker.value=textText.value;});
                }
                if(timerbgPicker&&timerbgText){
                    timerbgPicker.addEventListener('input',function(){timerbgText.value=timerbgPicker.value;});
                    timerbgText.addEventListener('input',function(){timerbgPicker.value=timerbgText.value;});
                }
                if(timertextPicker&&timertextText){
                    timertextPicker.addEventListener('input',function(){timertextText.value=timertextPicker.value;});
                    timertextText.addEventListener('input',function(){timertextPicker.value=timertextText.value;});
                }
            });
            </script>
            <?php
        }
    );
});
add_action('wp_body_open', function() {
    if (!get_option('announcement_bar_enabled')) return;
    $bg_color = get_option('announcement_bar_bg', '#222222');
    $text_color = get_option('announcement_bar_text', '#ffffff');
    $timerbg = get_option('announcement_bar_timerbg', '#ff4444');
    $timertext = get_option('announcement_bar_timertext', '#ffffff');
    $autoplay = get_option('announcement_bar_autoplay') ? 'true' : '0';
    $interval = intval(get_option('announcement_bar_interval', 5000));
    $posts = get_posts([
        'post_type' => 'announcement_bar',
        'post_status' => 'publish',
        'orderby' => 'menu_order',
        'order' => 'ASC',
        'numberposts' => -1
    ]);
    if (!$posts) return;
    echo '<div class="announcement-bar-swiper"><div class="swiper-wrapper">';
    foreach ($posts as $post) {
        $link = get_post_meta($post->ID, 'link', true);
        $text = esc_html($post->post_title);
        $enable_timer = get_post_meta($post->ID, 'enable_timer', true);
        $timer_end = get_post_meta($post->ID, 'timer_end', true);
        echo '<div class="swiper-slide bar-has-timer" style="color:'.$text_color.';background:'.$bg_color.';font-weight:bold;">';
        echo '<div class="bar-center-wrap">';
        echo '<span class="bar-main">'.($link ? '<a href="'.esc_url($link).'">'.$text.'</a>' : $text).'</span>';
        if ($enable_timer && $timer_end) {
            echo '<span class="bar-countdown" data-end="'.esc_attr($timer_end).'" data-bg="'.esc_attr($timerbg).'" data-txt="'.esc_attr($timertext).'"></span>';
        }
        echo '</div>';
        echo '</div>';
    }
    echo '</div></div>';
});
add_action('wp_footer', function() {
    $interval = intval(get_option('announcement_bar_interval', 5000));
    $autoplay = get_option('announcement_bar_autoplay') ? 'true' : '0';
    ?>
    <style>
    .announcement-bar-swiper {
        font-size: 14px; position: relative; overflow: hidden; z-index: 9999;
        width: 100vw; max-width: 100vw;
    }
    .announcement-bar-swiper .swiper-wrapper { display: flex;}
    .announcement-bar-swiper .swiper-slide {
        display: flex; justify-content: center; align-items: center;
        padding: 10px 20px; font-weight: bold; color: inherit;
        white-space: normal; word-break: break-all;
        font-size: 14px; box-sizing: border-box;
        width: 100vw !important; max-width: 100vw; min-width: 100vw;
    }
    .announcement-bar-swiper .bar-center-wrap {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 10px;
        width: 100%;
    }
    .announcement-bar-swiper .bar-main { display: inline-block; }
    .announcement-bar-swiper .swiper-slide * { color: inherit !important; font-size: inherit !important;}
    .announcement-bar-swiper a { color: inherit !important; text-decoration: none;}
    .announcement-bar-swiper .bar-countdown {
        display: inline-flex;
        align-items: center;
        gap: 6px;
        margin-left: 0;
        line-height: 1;
    }
    .announcement-bar-swiper .bar-countdown .time-num {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 32px;
        padding: 2px 8px;
        border-radius: 0; /* 去掉圆角 */
        font-family: monospace;
        font-weight: bold;
        font-size: inherit;
    }
    .announcement-bar-swiper .bar-countdown .time-label {
        margin: 0 2px;
        font-family: inherit;
        background: none !important;
        padding: 0;
        min-width: unset;
        border-radius: 0;
    }
    @media (max-width: 767.98px) {
        .announcement-bar-swiper { padding-left:0; padding-right:0;}
        .announcement-bar-swiper .swiper-wrapper {
            display: flex !important;
            flex-direction: row !important;
            width: 100vw;
        }
        .announcement-bar-swiper .swiper-slide {
            flex-direction: column !important;
            align-items: center !important;
            justify-content: center !important;
            gap: 0 !important;
            box-sizing: border-box;
            padding-left: 20px !important;
            padding-right: 20px !important;
            width: 100vw !important;
            max-width: 100vw !important;
            min-width: 100vw !important;
            margin: 0 !important;
        }
        .announcement-bar-swiper .bar-center-wrap {
            width: 100%;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 8px;
        }
        .announcement-bar-swiper .bar-main {
            width:100%;
            justify-content:center;
            text-align: center;
            padding-bottom: 0;
        }
        .announcement-bar-swiper .bar-countdown {
            width: 100%;
            margin: 0;
            justify-content: center;
            margin-bottom: 0;
        }
    }
    @media (min-width: 768px) {
        .announcement-bar-swiper .swiper-wrapper { display: flex;}
        .announcement-bar-swiper .swiper-slide {flex-direction: row !important;}
        .announcement-bar-swiper .bar-countdown {margin-left:10px;margin-top:0;}
    }
    </style>
    <script>
    document.addEventListener('DOMContentLoaded', function() {
        var autoplay = <?php echo $autoplay; ?>;
        var interval = <?php echo $interval; ?>;
        if (typeof Swiper !== 'undefined') {
            new Swiper('.announcement-bar-swiper', {
                loop: false,
                autoplay: autoplay ? { delay: interval } : false,
                slidesPerView: 1,
                centeredSlides: false,
                spaceBetween: 0,
            });
        }
        document.querySelectorAll('.bar-countdown').forEach(function(el){
            const end = new Date(el.dataset.end).getTime();
            const bg = el.dataset.bg || '#ff4444';
            const txt = el.dataset.txt || '#fff';
            function render(left){
                const d = Math.floor(left / (1000*60*60*24));
                const h = Math.floor((left / (1000*60*60)) % 24);
                const m = Math.floor((left / (1000*60)) % 60);
                const s = Math.floor((left / 1000) % 60);
                el.innerHTML =
                    '<span class="time-num" style="background:'+bg+';color:'+txt+';">'+String(d).padStart(2,'0')+'</span>' +
                    '<span class="time-label">D</span>' +
                    '<span class="time-num" style="background:'+bg+';color:'+txt+';">'+String(h).padStart(2,'0')+'</span>' +
                    '<span class="time-label">:</span>' +
                    '<span class="time-num" style="background:'+bg+';color:'+txt+';">'+String(m).padStart(2,'0')+'</span>' +
                    '<span class="time-label">:</span>' +
                    '<span class="time-num" style="background:'+bg+';color:'+txt+';">'+String(s).padStart(2,'0')+'</span>';
            }
            function update(){
                const now = new Date().getTime();
                const left = Math.max(0, end - now);
                render(left);
            }
            update();
            setInterval(update, 1000);
        });
    });
    </script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.css" />
    <script src="https://cdn.jsdelivr.net/npm/swiper@10/swiper-bundle.min.js"></script>
    <?php
});