You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法接入Preline UI站点主题切换Select组件求助

用Select组件实现Preline UI主题切换的解决方案

之前使用Preline UI的按钮下拉组件可正常切换主题,但改用Select组件后无法生效,以下是适配后的完整实现代码:


一、修改后的Header组件代码(替换原主题切换Dropdown为Select)

import { usePathname } from "next/navigation";
import Link from "next/link";
import Navigation from "./Navigation";
import { useEffect, useState } from "react";

export default function TheHeader() {
  const [selectedTheme, setSelectedTheme] = useState("default");

  useEffect(() => {
    // 从本地存储加载已保存的主题
    const savedTheme = localStorage.getItem('hs_theme') || 'default';
    setSelectedTheme(savedTheme);

    // 监听主题变化事件,同步Select选中状态
    const syncThemeSelect = (e) => {
      setSelectedTheme(e.detail);
    };
    window.addEventListener('on-hs-appearance-change', syncThemeSelect);
    return () => window.removeEventListener('on-hs-appearance-change', syncThemeSelect);
  }, []);

  // 处理Select选择变化
  const handleThemeSelect = (e) => {
    const theme = e.target.value;
    window.HSThemeAppearance.setAppearance(theme);
  };

  return (
    <>
      <header className="select-none z-50 bg-white dark:bg-zinc-900 fixed w-full h-16 border-b border-solid border-zinc-300/80 dark:border-zinc-800/80">
        <div className="px-20 h-full flex">
          <div className="self-center flex flex-1">
            <img src="/logo_header.png" alt="Logo" className="h-12" />
            <div id="name-version">
              <div className="text-lg lg:block ml-2 font-extrabold text-black dark:text-zinc-50">CodeCraft | Vanilla</div>
              <div className="lg:block ml-2 font-normal text-sm text-black dark:text-zinc-300 -mt-1">2 сезон</div>
            </div>
          </div>
          <Navigation />
          <div className="flex flex-1 justify-end items-center gap-4">
            {/* 主题切换Select组件 */}
            <div className="w-48">
              <select
                value={selectedTheme}
                onChange={handleThemeSelect}
                className="w-full px-3 py-2 rounded-lg border border-zinc-300 dark:border-zinc-700 bg-white dark:bg-zinc-800 text-sm text-zinc-900 dark:text-zinc-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
              >
                <option value="auto">自动(跟随系统)</option>
                <option value="default">浅色模式</option>
                <option value="dark">深色模式</option>
              </select>
            </div>

            <Link
              href='/login'
              className='relative flex h-10 py-4 px-7 justify-center items-center rounded-2xl bg-blue-600 self-center text-zinc-50 text-center text-sm not-italic font-semibold hover:bg-blue-700'
            >
              Авторизоваться
            </Link>
          </div>
        </div>
      </header>
      <script src="/ChangeTheme.js" />
    </>
  );
}

二、调整后的主题切换脚本(添加Select事件同步逻辑)

const HSThemeAppearance = {
    init() {
        const defaultTheme = 'default'
        let theme = localStorage.getItem('hs_theme') || defaultTheme

        if (document.querySelector('html').classList.contains('dark')) return
        this.setAppearance(theme)
    },
    _resetStylesOnLoad() {
        const $resetStyles = document.createElement('style')
        $resetStyles.innerText = `*{transition: unset !important;}`
        $resetStyles.setAttribute('data-hs-appearance-onload-styles', '')
        document.head.appendChild($resetStyles)
        return $resetStyles
    },
    setAppearance(theme, saveInStore = true, dispatchEvent = true) {
        const $resetStylesEl = this._resetStylesOnLoad()

        if (saveInStore) {
            localStorage.setItem('hs_theme', theme)
        }

        // 处理自动主题的实际生效值
        let activeTheme = theme;
        if (theme === 'auto') {
            activeTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default'
        }

        // 更新HTML的主题类
        document.querySelector('html').classList.remove('dark', 'default')
        document.querySelector('html').classList.add(activeTheme)

        setTimeout(() => {
            $resetStylesEl.remove()
        })

        if (dispatchEvent) {
            window.dispatchEvent(new CustomEvent('on-hs-appearance-change', {detail: theme}))
        }
    },
    getAppearance() {
        let theme = this.getOriginalAppearance()
        if (theme === 'auto') {
            theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default'
        }
        return theme
    },
    getOriginalAppearance() {
        const defaultTheme = 'default'
        return localStorage.getItem('hs_theme') || defaultTheme
    }
}
HSThemeAppearance.init()

// 监听系统主题变化,自动模式下同步更新
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
    if (HSThemeAppearance.getOriginalAppearance() === 'auto') {
        HSThemeAppearance.setAppearance('auto', false)
    }
})

window.addEventListener('load', () => {
    const $clickableThemes = document.querySelectorAll('[data-hs-theme-click-value]')
    const $switchableThemes = document.querySelectorAll('[data-hs-theme-switch]')
    const $themeSelects = document.querySelectorAll('select[onchange*="handleThemeSelect"]')

    // 保留原按钮切换逻辑
    $clickableThemes.forEach($item => {
        $item.addEventListener('click', () => HSThemeAppearance.setAppearance($item.getAttribute('data-hs-theme-click-value'), true, $item))
    })

    // 保留原开关切换逻辑
    $switchableThemes.forEach($item => {
        $item.addEventListener('change', (e) => {
            HSThemeAppearance.setAppearance(e.target.checked ? 'dark' : 'default')
        })
        $item.checked = HSThemeAppearance.getAppearance() === 'dark'
    })

    // 初始化Select选中值
    $themeSelects.forEach($select => {
        $select.value = HSThemeAppearance.getOriginalAppearance()
    })

    // 主题变化时同步所有控件状态
    window.addEventListener('on-hs-appearance-change', e => {
        $switchableThemes.forEach($item => {
            $item.checked = e.detail === 'dark'
        })
        $themeSelects.forEach($select => {
            $select.value = e.detail
        })
    })
})

三、tailwind.config.js保持原配置不变

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'media',
  content: [
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
 
    // Or if using `src` directory:
    "./src/**/*.{js,ts,jsx,tsx,mdx}",
    'node_modules/preline/dist/*.js',
  ],
  theme: {
    extend: {},
  },
  plugins: [
    require('tailwind-scrollbar'),
    require('preline/plugin'),
  ],
  
}

关键修改说明

  1. 组件层:用原生Select替换Dropdown,通过React状态管理选中值,监听主题变化事件保持状态同步,选择变化时直接调用Preline的主题切换方法。
  2. 脚本层:添加Select元素的初始化和状态同步逻辑,修复原setAppearance方法中主题类的设置逻辑,确保自动模式下正确应用系统主题。

内容的提问来源于stack exchange,提问作者zzhuravleff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:32:01