后端:新增cors,前端:新增设备判断钩子

This commit is contained in:
lsy 2025-01-16 22:36:51 +08:00
parent b75433cdbd
commit 5a6c020920
12 changed files with 164 additions and 64 deletions

2
.vscode/tasks.json vendored
View File

@ -35,7 +35,7 @@
"type": "shell", "type": "shell",
"command": "serve .", "command": "serve .",
"options": { "options": {
"cwd": "${workspaceFolder}/client/target/dx/client/release/web/public" "cwd": "${workspaceFolder}/target/dx/client/release/web/public"
}, },
"problemMatcher": [] "problemMatcher": []
}, },

28
Cargo.lock generated
View File

@ -3632,6 +3632,23 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "rocket_cors"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfac3a1df83f8d4fc96aa41dba3b86c786417b7fc0f52ec76295df2ba781aa69"
dependencies = [
"http 0.2.12",
"log",
"regex",
"rocket",
"serde",
"serde_derive",
"unicase",
"unicase_serde",
"url",
]
[[package]] [[package]]
name = "rocket_http" name = "rocket_http"
version = "0.5.1" version = "0.5.1"
@ -3988,6 +4005,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"common", "common",
"rocket", "rocket",
"rocket_cors",
"serde", "serde",
"surrealdb", "surrealdb",
"tokio", "tokio",
@ -4984,6 +5002,16 @@ version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]]
name = "unicase_serde"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef53697679d874d69f3160af80bc28de12730a985d57bdf2b47456ccb8b11f1"
dependencies = [
"serde",
"unicase",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.14" version = "1.0.14"

View File

@ -19,6 +19,7 @@ rocket = "0.5.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] } tokio = { version = "1.0", features = ["full"] }
surrealdb = "2.1.4" surrealdb = "2.1.4"
rocket_cors = "0.6.0"
[profile] [profile]

View File

@ -10,14 +10,9 @@ html[data-theme="dark"]{
background-color:black; background-color:black;
} }
@font-face {
font-family: 'AlimamaTi';
src: url('/assets/fonts/AlimamaFangYuanTiVF.ttf') format('truetype');
font-display: swap;
font-weight: 500;
}
html { html {
overflow-x:hidden; overflow-x:hidden;
font-family: 'AlimamaTi', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-weight: 500;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
} }

View File

@ -586,10 +586,6 @@ video {
top: 0px; top: 0px;
} }
.top-5 {
top: 1.25rem;
}
.top-\[0\.06rem\] { .top-\[0\.06rem\] {
top: 0.06rem; top: 0.06rem;
} }
@ -631,10 +627,6 @@ video {
display: grid; display: grid;
} }
.hidden {
display: none;
}
.h-1 { .h-1 {
height: 0.25rem; height: 0.25rem;
} }
@ -667,14 +659,14 @@ video {
min-width: 0px; min-width: 0px;
} }
.min-w-7 {
min-width: 1.75rem;
}
.flex-shrink-0 { .flex-shrink-0 {
flex-shrink: 0; flex-shrink: 0;
} }
.transform {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
@keyframes slide-in { @keyframes slide-in {
0% { 0% {
transform: translateX(100%); transform: translateX(100%);
@ -727,8 +719,8 @@ video {
gap: 0.25rem; gap: 0.25rem;
} }
.gap-10 { .gap-5 {
gap: 2.5rem; gap: 1.25rem;
} }
.justify-self-start { .justify-self-start {
@ -739,12 +731,20 @@ video {
justify-self: end; justify-self: end;
} }
.overflow-hidden {
overflow: hidden;
}
.truncate { .truncate {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.whitespace-nowrap {
white-space: nowrap;
}
.rounded { .rounded {
border-radius: 0.25rem; border-radius: 0.25rem;
} }
@ -832,8 +832,9 @@ video {
background-color: rgb(148 163 184 / var(--tw-bg-opacity, 1)); background-color: rgb(148 163 184 / var(--tw-bg-opacity, 1));
} }
.p-2 { .px-2 {
padding: 0.5rem; padding-left: 0.5rem;
padding-right: 0.5rem;
} }
.px-3 { .px-3 {
@ -906,6 +907,10 @@ video {
transition-duration: 150ms; transition-duration: 150ms;
} }
.duration-150 {
transition-duration: 150ms;
}
.duration-200 { .duration-200 {
transition-duration: 200ms; transition-duration: 200ms;
} }

View File

@ -1,36 +1,37 @@
use super::Toggle; use super::Toggle;
use crate::DeviceProvider;
use crate::Route; use crate::Route;
use dioxus::{logger::tracing, prelude::*}; use dioxus::{logger::tracing, prelude::*};
use global_attributes::dangerous_inner_html;
use wasm_bindgen::{prelude::Closure, JsCast}; use wasm_bindgen::{prelude::Closure, JsCast};
use web_sys::Event;
#[component] #[component]
pub fn Navbar() -> Element { pub fn Navbar() -> Element {
let mut progress_signal = use_signal(|| 0); let mut progress_signal = use_signal(|| 0);
let mut progress_hover = use_signal(|| false); let mut progress_hover = use_signal(|| false);
let mut is_more_single = use_signal(|| false);
// 监听滚动事件 let mut device_context = use_context::<Signal<DeviceProvider>>();
use_effect(move || { use_effect(move || {
let window = web_sys::window().unwrap(); let window = web_sys::window().unwrap();
let document = window.document().unwrap(); let document = window.document().unwrap();
let document_element = document.document_element().unwrap(); let document_element = document.document_element().unwrap();
let closure: Closure<dyn FnMut(_)> = { let closure = Closure::wrap(Box::new(move |_: Event| {
Closure::wrap(Box::new(move |_: web_sys::Event| { let screen_height = document_element.scroll_height() as f64;
let screen_height = document_element.scroll_height() as f64; let inner_height = window.inner_height().unwrap().as_f64().unwrap();
let inner_height = window.inner_height().unwrap().as_f64().unwrap(); let scroll_height = window.scroll_y().unwrap();
let scrool_height = window.scroll_y().unwrap(); let max_scroll_height = screen_height - inner_height;
let max_scroll_height = screen_height - inner_height;
let percent = if max_scroll_height > 0.0 { let percent = if max_scroll_height > 0.0 {
scrool_height / max_scroll_height scroll_height / max_scroll_height
} else { } else {
0.0 0.0
}; };
let percent = (percent.clamp(0.0, 1.0) * 100.0) as i32; let percent = (percent.clamp(0.0, 1.0) * 100.0) as i32;
progress_signal.set(percent); progress_signal.set(percent);
}) as Box<dyn FnMut(_)>) }) as Box<dyn FnMut(Event)>);
};
document document
.add_event_listener_with_callback("scroll", closure.as_ref().unchecked_ref()) .add_event_listener_with_callback("scroll", closure.as_ref().unchecked_ref())
@ -38,6 +39,8 @@ pub fn Navbar() -> Element {
closure.forget(); closure.forget();
}); });
let tp = r#"<div>hello,world</div>"#;
rsx! { rsx! {
nav { nav {
class: "border-red-500 bg-slate-400 text-xl fixed top-0 left-0 right-0", class: "border-red-500 bg-slate-400 text-xl fixed top-0 left-0 right-0",
@ -51,11 +54,7 @@ pub fn Navbar() -> Element {
} }
} }
div { div {
class:"flex gap-10 justify-center", class:"flex gap-5 justify-center",
Link {
to: Route::Home {},
"首页"
}
Link { Link {
to: Route::Home {}, to: Route::Home {},
"首页" "首页"
@ -64,6 +63,10 @@ pub fn Navbar() -> Element {
to: Route::Home {}, to: Route::Home {},
"首页" "首页"
} }
}
div{
dangerous_inner_html:"{tp}"
} }
div { div {

View File

@ -222,7 +222,7 @@ pub fn Notification() -> Element {
return rsx! { return rsx! {
div { div {
class: "w-[20rem] absolute right-8 top-5 max-sm:w-[12rem] ", class: "w-[20rem] absolute right-8 max-sm:w-[12rem]",
{notifications.notifications.iter().map(move |item| { {notifications.notifications.iter().map(move |item| {
rsx! { rsx! {
NotificationCard { NotificationCard {

View File

@ -3,12 +3,12 @@ use wasm_bindgen::{prelude::Closure, JsCast};
mod components; mod components;
mod utils; mod utils;
mod views; mod views;
use crate::utils::error::CustomResult;
use components::notification::{Notification, NotificationProvider}; use components::notification::{Notification, NotificationProvider};
use components::theme_toggle::{get_theme, ThemeProvider}; use components::theme_toggle::{get_theme, ThemeProvider};
use components::Navbar; use components::Navbar;
use utils::dom::{get_media_theme, set_element_dataset}; use utils::dom::{get_media_theme, set_element_dataset};
use views::Home; use views::Home;
use web_sys::Event;
#[derive(Debug, Clone, Routable, PartialEq)] #[derive(Debug, Clone, Routable, PartialEq)]
enum Route { enum Route {
@ -22,6 +22,42 @@ const FAVICON: Asset = asset!("/assets/favicon.ico");
const GLOBAL_CSS: Asset = asset!("/assets/styling/global.css"); const GLOBAL_CSS: Asset = asset!("/assets/styling/global.css");
const TAILWIND_CSS: Asset = asset!("/assets/styling/tailwind.css"); const TAILWIND_CSS: Asset = asset!("/assets/styling/tailwind.css");
#[derive(Debug, Clone, PartialEq)]
enum DeviceType {
DESKTOP,
MOBILE,
}
impl std::fmt::Display for DeviceType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
DeviceType::DESKTOP => "desktop",
DeviceType::MOBILE => "mobile",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, PartialEq)]
struct DeviceProvider(pub DeviceType);
impl Default for DeviceProvider {
fn default() -> Self {
DeviceProvider(DeviceType::DESKTOP)
}
}
impl From<f64> for DeviceProvider {
fn from(value: f64) -> Self {
let derive_type = if value > 1024.0 {
DeviceType::DESKTOP
} else {
DeviceType::MOBILE
};
DeviceProvider(derive_type)
}
}
fn main() { fn main() {
dioxus::launch(App); dioxus::launch(App);
} }
@ -31,7 +67,24 @@ fn App() -> Element {
let theme = get_theme(); let theme = get_theme();
use_context_provider(|| Signal::new(ThemeProvider(theme.clone()))); use_context_provider(|| Signal::new(ThemeProvider(theme.clone())));
use_context_provider(|| Signal::new(NotificationProvider::default())); use_context_provider(|| Signal::new(NotificationProvider::default()));
use_context_provider(|| Signal::new(DeviceProvider::default()));
let mut is_dark_context = use_context::<Signal<ThemeProvider>>(); let mut is_dark_context = use_context::<Signal<ThemeProvider>>();
let mut dervice_context = use_context::<Signal<DeviceProvider>>();
use_effect(move || {
let window = web_sys::window().unwrap();
let width = window.inner_width().unwrap().as_f64().unwrap();
dervice_context.set(width.into());
let closure = Closure::wrap(Box::new(move |_: Event| {
let width = window.inner_width().unwrap().as_f64().unwrap();
dervice_context.set(width.into());
}) as Box<dyn FnMut(Event)>);
let window = web_sys::window().unwrap();
window
.add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref())
.unwrap();
closure.forget();
});
use_effect(move || { use_effect(move || {
let _ = set_element_dataset("html", "theme", &theme); let _ = set_element_dataset("html", "theme", &theme);

View File

@ -10,3 +10,4 @@ rocket = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
surrealdb = { workspace = true } surrealdb = { workspace = true }
rocket_cors = { workspace = true }

View File

@ -2,9 +2,8 @@ mod db;
mod utils; mod utils;
use db::Database; use db::Database;
use rocket::{get, routes, State}; use rocket::{get, http::Method, routes};
use serde::{Deserialize, Serialize}; use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors, CorsOptions};
use std::sync::{Arc, Mutex};
use utils::error::CustomResult; use utils::error::CustomResult;
@ -13,15 +12,29 @@ fn index() -> &'static str {
"Hello, world!" "Hello, world!"
} }
#[get("/api/theme")]
fn get_theme() -> &'static str {
"light"
}
pub struct AppState { pub struct AppState {
db: Database, db: Database,
} }
fn cors() -> Cors {
CorsOptions {
allowed_origins: AllowedOrigins::all(),
allowed_methods: vec![Method::Get, Method::Post, Method::Options]
.into_iter()
.map(From::from)
.collect(),
allowed_headers: AllowedHeaders::all(),
allow_credentials: true,
expose_headers: Default::default(),
max_age: None,
send_wildcard: false,
fairing_route_base: "/".to_string(),
fairing_route_rank: 0,
}
.to_cors()
.expect("CORS配置错误")
}
#[rocket::main] #[rocket::main]
async fn main() -> CustomResult<()> { async fn main() -> CustomResult<()> {
let rocket_build = rocket::build(); let rocket_build = rocket::build();
@ -29,8 +42,9 @@ async fn main() -> CustomResult<()> {
let db = Database::link().await?; let db = Database::link().await?;
let rocket = rocket_build let rocket = rocket_build
.mount("/", routes![index, get_theme]) .mount("/", routes![index])
.manage(AppState { db }) .manage(AppState { db })
.attach(cors())
.ignite() .ignite()
.await?; .await?;
rocket.launch().await?; rocket.launch().await?;