前端:优化错误处理,提取web_sys操作,完善主题切换,

This commit is contained in:
lsy 2024-12-22 00:43:10 +08:00
parent 351ee3f675
commit e7ebf60586
17 changed files with 148 additions and 106 deletions

22
.vscode/tasks.json vendored
View File

@ -24,16 +24,34 @@
{
"label": "frontend-dev",
"type": "shell",
"command": "dx serve --platform web",
"command": "dx serve",
"options": {
"cwd": "${workspaceFolder}/frontend"
},
"problemMatcher": []
},
{
"label": "frontend-start",
"type": "shell",
"command": "serve .",
"options": {
"cwd": "${workspaceFolder}/frontend/target/dx/frontend/release/web/public"
},
"problemMatcher": []
},
{
"label": "frontend-build",
"type": "shell",
"command": "dx build --platform web",
"command": "dx build --release",
"options": {
"cwd": "${workspaceFolder}/frontend"
},
"problemMatcher": []
},
{
"label": "frontend-tailwind-build",
"type": "shell",
"command": "npx tailwindcss -i ./tailwind.css -o ./assets/styling/tailwind.css",
"options": {
"cwd": "${workspaceFolder}/frontend"
},

1
frontend/Cargo.lock generated
View File

@ -1474,6 +1474,7 @@ version = "0.1.0"
dependencies = [
"dioxus",
"dioxus-free-icons",
"wasm-bindgen",
"web-sys",
]

View File

@ -9,11 +9,11 @@ edition = "2021"
[dependencies]
dioxus = { version = "0.6.0", features = ["router"] }
dioxus-free-icons = { version = "0.9", features = ["bootstrap"] }
web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList"] }
wasm-bindgen = "0.2.99"
web-sys = { version = "0.3.76", features = ["Window","Storage","MediaQueryList","Document"] }
[features]
default = []
default = ["dioxus/web"]
web = ["dioxus/web"]
desktop = ["dioxus/desktop"]
mobile = ["dioxus/mobile"]

View File

@ -1,8 +0,0 @@
#blog {
margin-top: 50px;
}
#blog a {
color: #ffffff;
margin-top: 50px;
}

View File

@ -1,5 +0,0 @@
#navbar {
display: flex;
flex-direction: row;
}

View File

@ -554,21 +554,12 @@ video {
display: none;
}
.static {
position: static;
}
.bg-sky-700 {
.bg-yellow-200 {
--tw-bg-opacity: 1;
background-color: rgb(3 105 161 / var(--tw-bg-opacity, 1));
background-color: rgb(254 240 138 / var(--tw-bg-opacity, 1));
}
.bg-white {
.dark\:bg-sky-500:is(.dark *) {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));
}
.text-slate-100 {
--tw-text-opacity: 1;
color: rgb(241 245 249 / var(--tw-text-opacity, 1));
background-color: rgb(14 165 233 / var(--tw-bg-opacity, 1));
}

View File

@ -0,0 +1,44 @@
use super::error::{CustomErrorInto, CustomResult};
use web_sys::{window, Document, MediaQueryList, Storage, Window};
fn get_window() -> CustomResult<Window> {
Ok(window().ok_or("浏览器window对象不存在")?)
}
fn get_storage() -> CustomResult<Storage> {
get_window()?
.local_storage()?
.ok_or("浏览器不支持localStorage".into_custom_error())
}
pub fn get_local_storage_value(key: &str) -> CustomResult<String> {
get_storage()?
.get_item(key)?
.ok_or(format!("localStorage中不存在键'{}'", key).into_custom_error())
}
pub fn set_local_storage_value(key: &str, value: &str) -> CustomResult<()> {
Ok(get_storage()?.set_item(key, value)?)
}
pub fn get_media_theme() -> CustomResult<String> {
let media_query = get_window()?
.match_media("(prefers-color-scheme: dark)")?
.ok_or("查询media时发生错误".into_custom_error())?
.matches();
if media_query {
return Ok("dark".to_string());
}
Ok("light".to_string())
}
pub fn set_element_class(ele_name: &str, class_name: &str) -> CustomResult<()> {
get_window()?
.document()
.ok_or("浏览器document对象不存在".into_custom_error())?
.query_selector(ele_name)?
.ok_or(format!("获取元素{}失败", ele_name).into_custom_error())?
.set_class_name(class_name);
Ok(())
}

View File

@ -1,3 +1,5 @@
use wasm_bindgen::JsValue;
#[derive(Debug)]
pub struct CustomError(String);
@ -17,11 +19,26 @@ impl CustomErrorInto for &str {
}
}
impl<E: std::error::Error> From<E> for CustomError {
fn from(error: E) -> Self {
impl From<std::io::Error> for CustomError {
fn from(error: std::io::Error) -> Self {
CustomError(error.to_string())
}
}
impl From<JsValue> for CustomError {
fn from(error: JsValue) -> Self {
CustomError(
error
.as_string()
.unwrap_or_else(|| String::from("Unknown JS error")),
)
}
}
impl From<&str> for CustomError {
fn from(error: &str) -> Self {
CustomError(error.to_string())
}
}
pub type CustomResult<T> = Result<T, CustomError>;

View File

@ -1 +1,2 @@
pub mod dom;
pub mod error;

View File

@ -1,4 +1,4 @@
mod navbar;
pub mod navbar;
pub use navbar::Navbar;
mod theme_toggle;
pub use theme_toggle::Toggle;
pub mod theme_toggle;
pub use theme_toggle::Toggle;

View File

@ -1,13 +1,10 @@
use super::Toggle;
use crate::Route;
use dioxus::prelude::*;
const NAVBAR_CSS: Asset = asset!("/assets/styling/navbar.css");
use super::Toggle;
#[component]
pub fn Navbar() -> Element {
rsx! {
document::Link { rel: "stylesheet", href: NAVBAR_CSS }
div {
id: "navbar",

View File

@ -1,8 +1,10 @@
use crate::common::dom::{set_element_class, set_local_storage_value};
use dioxus::{logger::tracing, prelude::*};
use dioxus_free_icons::icons::bs_icons::BsMoonStars;
use dioxus_free_icons::icons::bs_icons::BsSun;
use dioxus_free_icons::Icon;
use crate::{IsDark,set_local_storage_value};
#[derive(Debug, Clone, PartialEq)]
pub struct IsDark(pub String);
#[derive(PartialEq, Props, Clone)]
pub struct ToggleProps {
@ -16,24 +18,38 @@ pub struct ToggleProps {
#[component]
pub fn Toggle(props: ToggleProps) -> Element {
let mut dark_context=use_context::<Signal<IsDark>>();
let mut dark_context = use_context::<Signal<IsDark>>();
rsx! {
div {
onclick: move |_|
onclick: move |_|
{
dark_context.set(IsDark(!dark_context().0));
match set_local_storage_value("theme", if !dark_context().0 { "true" } else { "false" }) {
let theme;
if dark_context().0=="light" {
theme="dark"
}else {
theme="light"
};
dark_context.set(IsDark(theme.to_string()));
match set_local_storage_value("theme",theme) {
Ok(_)=>{},
Err(_)=>{
tracing::error!("主题储存失败");
},
}
match set_element_class("html",theme) {
Ok(_)=>{},
Err(e)=>{
tracing::error!("主题类名设置失败:{}",e);
},
}
}
,
{
match {dark_context().0} {
false=>{
match {dark_context().0.as_str()} {
"dark"=>{
rsx!(
Icon{
width:props.width,
@ -43,7 +59,7 @@ pub fn Toggle(props: ToggleProps) -> Element {
}
)
},
true=>{
_=>{
rsx!{
Icon{
width:props.width,

View File

@ -1,4 +0,0 @@
@import "@radix-ui/themes/styles.css";
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,7 +1,7 @@
use dioxus::{logger::tracing, prelude::*};
use web_sys::{window, MediaQueryList, Storage};
mod common;
use common::error::{CustomErrorInto, CustomResult};
use common::dom::{get_local_storage_value, get_media_theme, set_element_class};
use components::theme_toggle::IsDark;
use components::Navbar;
use views::Home;
@ -20,41 +20,7 @@ enum Route {
const FAVICON: Asset = asset!("/assets/favicon.ico");
const GLOBAL_CSS: Asset = asset!("/assets/styling/global.css");
const TAILWIND_CSS: Asset = asset!("/assets/tailwind.css");
#[derive(Debug, Clone, PartialEq)]
pub struct IsDark(bool);
fn get_storage() -> CustomResult<Storage> {
window()
.ok_or("浏览器window对象不存在".into_custom_error())?
.local_storage()
.map_err(|_| "无法访问localStorage API".into_custom_error())?
.ok_or("浏览器不支持localStorage".into_custom_error())
}
pub fn get_local_storage_value(key: &str) -> CustomResult<String> {
get_storage()?
.get_item(key)
.map_err(|_| "读取localStorage时发生错误".into_custom_error())?
.ok_or(format!("localStorage中不存在键'{}'", key).into_custom_error())
}
pub fn set_local_storage_value(key: &str, value: &str) -> CustomResult<()> {
get_storage()?
.set_item(key, value)
.map_err(|_| format!("无法将值写入localStorage的'{}'键", key).into_custom_error())
}
pub fn get_media_theme() -> CustomResult<bool> {
let media_query = window()
.ok_or("浏览器window对象不存在".into_custom_error())?
.match_media("(prefers-color-scheme: dark)")
.map_err(|_| format!("读取媒体查询时发生错误").into_custom_error())?
.ok_or("查询media时发生错误".into_custom_error())?
.matches();
Ok(media_query)
}
const TAILWIND_CSS: Asset = asset!("/assets/styling/tailwind.css");
fn main() {
dioxus::launch(App);
@ -62,32 +28,33 @@ fn main() {
#[component]
fn App() -> Element {
use_context_provider(|| Signal::new(IsDark(false)));
use_context_provider(|| Signal::new(IsDark("light".to_string())));
let mut is_dark_context = use_context::<Signal<IsDark>>();
#[cfg(target_arch = "wasm32")]
{
let _ = use_memo(move || {
use_effect(move || {
let theme = {
let storage_theme = get_local_storage_value("theme");
match storage_theme {
Ok(b) => is_dark_context.set(IsDark(b == "true")),
Ok(s) => s,
Err(_) => {
let device_theme = get_media_theme();
match device_theme {
Ok(b) => is_dark_context.set(IsDark(b)),
Err(_) => is_dark_context.set(IsDark(false))
Ok(s) => s,
Err(_) => "light".to_string(),
}
}
}
});
}
};
is_dark_context.set(IsDark(theme.clone()));
let _ = set_element_class("html", &theme);
});
rsx! {
// Global app resources
document::Link { rel: "icon", href: FAVICON }
document::Stylesheet{ href: GLOBAL_CSS }
document::Stylesheet { href: TAILWIND_CSS }
Router::<Route> {}
div {
document::Link { rel: "icon", href: FAVICON }
document::Stylesheet{ href: GLOBAL_CSS }
document::Stylesheet { href: TAILWIND_CSS }
Router::<Route> {}
}
}
}

View File

@ -3,6 +3,12 @@ use dioxus::prelude::*;
#[component]
pub fn Home() -> Element {
rsx! {
div { "hello,world" }
div {
class:"bg-yellow-200 dark:bg-sky-500",
"hello,world"
ul {
li { "nihao" }
}
}
}
}

View File

@ -6,4 +6,5 @@ module.exports = {
extend: {},
},
plugins: [],
darkMode: ['class'],
};