add chart
This commit is contained in:
14
Cargo.toml
14
Cargo.toml
@@ -6,13 +6,25 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
dioxus = {version = "0.5", features = ["web", "router"]} #"fermi"
|
||||
dioxus = {version = "0.5", features = ["web", "router"]}
|
||||
console_error_panic_hook = "0.1"
|
||||
dioxus-html-macro = "0.3"
|
||||
|
||||
dioxus-sdk = "*"
|
||||
|
||||
tracing = "0"
|
||||
tracing-wasm = "0"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
tokio = {version = "1", features = ["time"]}
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
async-std = "1"
|
||||
instant = {version = "^0.1", features = ["wasm-bindgen"]}
|
||||
|
||||
[profile.release]
|
||||
opt-level = 'z'
|
||||
lto = true
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link data-trunk href="assets/favicon.ico" rel="copy-file">
|
||||
<link data-trunk href="input.css" rel="tailwind-css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
|
||||
<title>R-Dashboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<!-- <div class="lg:hidden bg-black"></div> -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
13
src/app.rs
13
src/app.rs
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* @Date: 2022-10-11 00:07:29
|
||||
* @LastEditTime: 2024-07-06 18:46:11
|
||||
* @LastEditTime: 2024-07-11 09:37:23
|
||||
* @Description:
|
||||
*/
|
||||
#![allow(non_snake_case)]
|
||||
@@ -26,24 +26,17 @@ fn Login() -> Element {
|
||||
login::view()
|
||||
}
|
||||
|
||||
// fn Dashboard() -> Element {
|
||||
// let router = router();
|
||||
// let url = router.current_route_string();
|
||||
// tracing::warn!("url: {}", url);
|
||||
// Home("dashboard")
|
||||
// }
|
||||
|
||||
#[component]
|
||||
fn NotFound(segments: Vec<String>) -> Element {
|
||||
tracing::info!("segments: {:?}", segments);
|
||||
if let Some(url) = segments.get(0) {
|
||||
if let Some(url) = segments.first() {
|
||||
Body(url)
|
||||
} else {
|
||||
Body("dashboard")
|
||||
}
|
||||
}
|
||||
|
||||
// Home Page View
|
||||
// Body Page View
|
||||
fn Body(url: impl AsRef<str>) -> Element {
|
||||
let url = url.as_ref();
|
||||
rsx! {
|
||||
|
||||
157
src/components/menu/icons.rs
Normal file
157
src/components/menu/icons.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* @Date: 2024-07-10 22:51:50
|
||||
* @LastEditTime: 2024-07-10 22:51:57
|
||||
*/
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html_macro::html;
|
||||
|
||||
pub fn icon_logo() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-12 h-12"
|
||||
view_box="0 0 512 512"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M364.61 390.213C304.625 450.196 207.37 450.196 147.386 390.213C117.394 360.22 102.398 320.911 102.398 281.6C102.398 242.291 117.394 202.981 147.386 172.989C147.386 230.4 153.6 281.6 230.4 307.2C230.4 256 256 102.4 294.4 76.7999C320 128 334.618 142.997 364.608 172.989C394.601 202.981 409.597 242.291 409.597 281.6C409.597 320.911 394.601 360.22 364.61 390.213Z"
|
||||
fill="#4C51BF"
|
||||
stroke="#4C51BF"
|
||||
stroke_width="2"
|
||||
stroke_linecap="round"
|
||||
stroke_linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M201.694 387.105C231.686 417.098 280.312 417.098 310.305 387.105C325.301 372.109 332.8 352.456 332.8 332.8C332.8 313.144 325.301 293.491 310.305 278.495C295.309 263.498 288 256 275.2 230.4C256 243.2 243.201 320 243.201 345.6C201.694 345.6 179.2 332.8 179.2 332.8C179.2 352.456 186.698 372.109 201.694 387.105Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
pub fn icon_chart() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 10C2 5.58172 5.58172 2 10 2V10H18C18 14.4183 14.4183 18 10 18C5.58172 18 2 14.4183 2 10Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12 2.25195C14.8113 2.97552 17.0245 5.18877 17.748 8.00004H12V2.25195Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_element() -> Element {
|
||||
html!(
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5 3C3.89543 3 3 3.89543 3 5V7C3 8.10457 3.89543 9 5 9H7C8.10457 9 9 8.10457 9 7V5C9 3.89543 8.10457 3 7 3H5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M5 11C3.89543 11 3 11.8954 3 13V15C3 16.1046 3.89543 17 5 17H7C8.10457 17 9 16.1046 9 15V13C9 11.8954 8.10457 11 7 11H5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11 5C11 3.89543 11.8954 3 13 3H15C16.1046 3 17 3.89543 17 5V7C17 8.10457 16.1046 9 15 9H13C11.8954 9 11 8.10457 11 7V5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11 13C11 11.8954 11.8954 11 13 11H15C16.1046 11 17 11.8954 17 13V15C17 16.1046 16.1046 17 15 17H13C11.8954 17 11 16.1046 11 15V13Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_table() -> Element {
|
||||
html!(
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7 3C6.44772 3 6 3.44772 6 4C6 4.55228 6.44772 5 7 5H13C13.5523 5 14 4.55228 14 4C14 3.44772 13.5523 3 13 3H7Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M4 7C4 6.44772 4.44772 6 5 6H15C15.5523 6 16 6.44772 16 7C16 7.55228 15.5523 8 15 8H5C4.44772 8 4 7.55228 4 7Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2 11C2 9.89543 2.89543 9 4 9H16C17.1046 9 18 9.89543 18 11V15C18 16.1046 17.1046 17 16 17H4C2.89543 17 2 16.1046 2 15V11Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_form() -> Element {
|
||||
html!(
|
||||
<svg class="w-5 h-5" fill="currentColor" view_box="0 0 20 20">
|
||||
<path
|
||||
d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"
|
||||
/>
|
||||
<path
|
||||
fill_rule="evenodd"
|
||||
d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"
|
||||
clip_rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_card() -> Element {
|
||||
html!(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" view_box="0 0 20 20" fill="currentColor">
|
||||
<path d="M4 4a2 2 0 00-2 2v1h16V6a2 2 0 00-2-2H4z" />
|
||||
<path fill_rule="evenodd" d="M18 9H2v5a2 2 0 002 2h12a2 2 0 002-2V9zM4 13a1 1 0 011-1h1a1 1 0 110 2H5a1 1 0 01-1-1zm5-1a1 1 0 100 2h1a1 1 0 100-2H9z"
|
||||
clip_rule="evenodd" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_model() -> Element {
|
||||
html!(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" view_box="0 0 20 20" fill="currentColor">
|
||||
<path d="M3 12v3c0 1.657 3.134 3 7 3s7-1.343 7-3v-3c0 1.657-3.134 3-7 3s-7-1.343-7-3z" />
|
||||
<path d="M3 7v3c0 1.657 3.134 3 7 3s7-1.343 7-3V7c0 1.657-3.134 3-7 3S3 8.657 3 7z" />
|
||||
<path d="M17 5c0 1.657-3.134 3-7 3S3 6.657 3 5s3.134-3 7-3 7 1.343 7 3z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_blank() -> Element {
|
||||
html!(
|
||||
<svg class="w-5 h-5" fill="currentColor" view_box="0 0 20 20">
|
||||
<path
|
||||
d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// #[inline_props]
|
||||
pub fn icon_up_down() -> Element {
|
||||
html!(
|
||||
<svg class="w-3 h-3"
|
||||
fill="currentColor"
|
||||
view_box="0 0 12 12">
|
||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
65
src/components/menu/index.rs
Normal file
65
src/components/menu/index.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* @Date: 2024-07-09 22:37:33
|
||||
* @LastEditTime: 2024-07-11 09:37:45
|
||||
*/
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html_macro::html;
|
||||
|
||||
use crate::components::menu::icons;
|
||||
|
||||
use super::MenuItem;
|
||||
|
||||
pub fn View(menus: Vec<MenuItem>) -> Element {
|
||||
html! {
|
||||
<nav class="mt-10">
|
||||
{menus.into_iter().map(render_menu_item)}
|
||||
</nav>
|
||||
}
|
||||
}
|
||||
|
||||
fn render_menu_item(item: MenuItem) -> Element {
|
||||
#[allow(deprecated)]
|
||||
let route = use_router();
|
||||
let route_name = route.current_route_string();
|
||||
|
||||
let mut menu_open = use_signal(|| false);
|
||||
let toggle_menu = if menu_open() { "block" } else { "hidden" };
|
||||
|
||||
let highlight_class = |e: &str| {
|
||||
match e == route_name {
|
||||
true => "flex items-center px-6 py-2 mt-2 duration-200 border-l-4 bg-gray-600 bg-opacity-25 text-gray-100 border-gray-100",
|
||||
false => "flex items-center px-6 py-2 mt-2 duration-200 border-l-4 border-gray-900 text-gray-500 hover:bg-gray-600 hover:bg-opacity-25 hover:text-gray-100",
|
||||
}
|
||||
};
|
||||
|
||||
let children = item.children;
|
||||
html! {
|
||||
<Link class = {highlight_class(&item.key)}
|
||||
to={&item.key}
|
||||
onclick = { move|_| { menu_open.set(!menu_open())}}
|
||||
>
|
||||
{ item.icon}
|
||||
<span class="mx-4">{ item.label }</span>
|
||||
{if !children.is_empty() {
|
||||
html! {
|
||||
<div class = {format!("{}", if menu_open() { "rotate-180 mt-1" } else { "" })}>
|
||||
{icons::icon_up_down()}
|
||||
</div>
|
||||
}
|
||||
}else {
|
||||
html! {}
|
||||
}}
|
||||
</Link>
|
||||
{ if !children.is_empty() {
|
||||
html! {
|
||||
<div class="ml-8 mt-1 { toggle_menu }">
|
||||
{children.into_iter().map(render_menu_item) }
|
||||
</div>
|
||||
}
|
||||
} else {
|
||||
html! {}
|
||||
}}
|
||||
|
||||
}
|
||||
}
|
||||
92
src/components/menu/mod.rs
Normal file
92
src/components/menu/mod.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* @Date: 2024-07-10 09:14:38
|
||||
* @LastEditTime: 2024-07-10 22:48:32
|
||||
*/
|
||||
|
||||
pub mod icons;
|
||||
mod index;
|
||||
use dioxus::dioxus_core::Element;
|
||||
pub use index::*;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MenuVo {
|
||||
pub id: i32,
|
||||
pub icon: String,
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub api_url: String,
|
||||
pub menu_type: i32,
|
||||
pub parent_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct MenuItem {
|
||||
pub id: i32,
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub icon: Element,
|
||||
pub parent_id: i32,
|
||||
pub children: Vec<MenuItem>,
|
||||
}
|
||||
|
||||
impl From<MenuVo> for MenuItem {
|
||||
fn from(item: MenuVo) -> Self {
|
||||
MenuItem {
|
||||
id: item.id,
|
||||
key: item.path,
|
||||
label: item.name,
|
||||
icon: icons::icon_blank(),
|
||||
parent_id: item.parent_id,
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_tree(data: Vec<MenuItem>, pid: i32) -> Vec<MenuItem> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for item in data.iter() {
|
||||
if item.parent_id == pid {
|
||||
let mut node = item.clone();
|
||||
node.children = build_tree(data.clone(), item.id);
|
||||
result.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_test() {
|
||||
// let data = vec![
|
||||
// MenuItem {
|
||||
// id: 1,
|
||||
// key: "item1".to_string(),
|
||||
// label: "Item 1".to_string(),
|
||||
// icon: "icon1".to_string(),
|
||||
// parent_id: 0,
|
||||
// children: vec![],
|
||||
// },
|
||||
// MenuItem {
|
||||
// id: 2,
|
||||
// key: "item2".to_string(),
|
||||
// label: "Item 2".to_string(),
|
||||
// icon: "icon2".to_string(),
|
||||
// parent_id: 1,
|
||||
// children: vec![],
|
||||
// },
|
||||
// MenuItem {
|
||||
// id: 3,
|
||||
// key: "item3".to_string(),
|
||||
// label: "Item 3".to_string(),
|
||||
// icon: "icon3".to_string(),
|
||||
// parent_id: 1,
|
||||
// children: vec![],
|
||||
// },
|
||||
// ];
|
||||
|
||||
// let tree = build_tree(data, 0);
|
||||
// dbg!("{:#?}", tree);
|
||||
}
|
||||
169
src/components/menu2.rs
Normal file
169
src/components/menu2.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
use dioxus::prelude::*;
|
||||
// use reqwest::Client;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Menus {
|
||||
pub items: Vec<MenuItem>,
|
||||
pub on_click: EventHandler<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MenuItem {
|
||||
pub key: String,
|
||||
pub icon: String,
|
||||
pub label: String,
|
||||
pub parent_key: Option<String>,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Menu(cx: Menus) -> Element {
|
||||
let render_item = |item: &MenuItem| {
|
||||
let onclick = cx.on_click.clone();
|
||||
let key = item.key.clone();
|
||||
let children = find_children(&cx.items, &key);
|
||||
let mut menu_open = use_signal(|| vec![false, false]);
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
class: "flex items-center px-6 py-2 mt-4 duration-200 border-l-4 border-gray-900 text-gray-500 hover:bg-gray-600 hover:bg-opacity-25 hover:text-gray-100 cursor-pointer",
|
||||
onclick: move |_| onclick.call(key.clone()),
|
||||
div {
|
||||
class: "flex items-center space-x-4",
|
||||
span { "{item.icon}" }, // 这里假设图标是一个字符串
|
||||
span { "{item.label}" }
|
||||
if !children.is_empty() {
|
||||
div {
|
||||
class: format!("{}", if menu_open()[0] { "rotate-180" } else { "" }),
|
||||
icons::icon_up_down {}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !children.is_empty() {
|
||||
div {
|
||||
class: format!("ml-8 mt-1 {}", if menu_open()[0] { "block" } else { "hidden" }),
|
||||
{children}.iter().map(render_item).collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rsx! {
|
||||
nav {
|
||||
class: "mt-10",
|
||||
// {cx}.items.iter().filter(|item| item.parent_key.is_none()).map(render_item).collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_children<'a>(items: &'a [MenuItem], parent_key: &str) -> Vec<&'a MenuItem> {
|
||||
items
|
||||
.iter()
|
||||
.filter(|item| item.parent_key.as_ref().map_or(false, |key| key == parent_key))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_tree(items: Vec<MenuItem>) -> Vec<MenuItem> {
|
||||
let mut map: HashMap<String, MenuItem> = HashMap::new();
|
||||
let mut roots: Vec<MenuItem> = Vec::new();
|
||||
|
||||
for item in items {
|
||||
map.insert(item.key.clone(), item.clone());
|
||||
}
|
||||
|
||||
for item in items {
|
||||
if let Some(parent_key) = &item.parent_key {
|
||||
if let Some(parent) = map.get_mut(parent_key) {
|
||||
if parent.children.is_none() {
|
||||
parent.children = Some(Vec::new());
|
||||
}
|
||||
if let Some(children) = &mut parent.children {
|
||||
children.push(item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
roots.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
roots
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
// fn app(cx: Scope) -> Element {
|
||||
// let menu_items = use_state(cx, || vec![]);
|
||||
|
||||
// use_effect(cx, (), |_| {
|
||||
// to_owned![menu_items];
|
||||
// async move {
|
||||
// let client = Client::new();
|
||||
// let response = client
|
||||
// .get("https://your-api-endpoint.com/menu")
|
||||
// .send()
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .json::<Vec<MenuItem>>()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let tree = build_tree(response);
|
||||
// menu_items.set(tree);
|
||||
// }
|
||||
// });
|
||||
|
||||
// cx.render(rsx! {
|
||||
// Menu {
|
||||
// items: menu_items.get().clone(),
|
||||
// on_click: move |key| {
|
||||
// // 处理点击事件
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
mod icons {
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html_macro::html;
|
||||
pub fn icon_up_down() -> Element {
|
||||
html!(
|
||||
<svg class="w-3 h-3"
|
||||
fill="currentColor"
|
||||
view_box="0 0 12 12">
|
||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn render_menu_item(item: MenuItem) -> Element {
|
||||
let children = item.children;
|
||||
let mut menu_open = use_signal(|| false);
|
||||
let toggle_menu = if menu_open() { "block" } else { "hidden" };
|
||||
tracing::info!("menu_open: {}", toggle_menu);
|
||||
rsx! {
|
||||
div{
|
||||
class:"flex items-center px-6 py-2 mt-4 duration-200 border-l-4 border-gray-900 text-gray-500 hover:bg-gray-600 hover:bg-opacity-25 hover:text-gray-100 cursor-pointer ",
|
||||
|
||||
// aria_controls="dropdown-example"
|
||||
"x-data": "{{ open: false }}",
|
||||
|
||||
a{ id: "dropdown-example", class:"flex items-center space-x-4",
|
||||
href:{item.key},
|
||||
// onclick = { move|_| { menu_open.set(!menu_open())}}>
|
||||
{ item.icon}
|
||||
span{ class:"mx-4", { item.label } }
|
||||
}
|
||||
|
||||
}
|
||||
{ if !children.is_empty() {
|
||||
rsx! {
|
||||
div{ class:"ml-8 mt-1 { toggle_menu }",
|
||||
{children.into_iter().map(|item| render_menu_item(item)) }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rsx! {}
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,13 @@
|
||||
/*
|
||||
* @Date: 2022-10-11 23:13:17
|
||||
* @LastEditTime: 2024-07-06 00:05:28
|
||||
* @LastEditTime: 2024-07-09 22:37:24
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
use dioxus::signals::{GlobalSignal, Signal};
|
||||
|
||||
pub mod header;
|
||||
pub mod menu;
|
||||
pub mod sidebar;
|
||||
|
||||
// #[derive(Clone, Copy)]
|
||||
|
||||
@@ -1,26 +1,20 @@
|
||||
/*
|
||||
* @Date: 2022-10-11 18:53:17
|
||||
* @LastEditTime: 2024-07-07 21:42:00
|
||||
* @LastEditTime: 2024-07-11 10:48:17
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::{components::menu, modules::demo_data::MENUS};
|
||||
|
||||
use super::SIDEBAR_OPEN;
|
||||
|
||||
pub fn view() -> Element {
|
||||
let route = router();
|
||||
let route_name = route.current_route_string();
|
||||
// 共享状态
|
||||
let mut sidebar_open = use_hook(|| SIDEBAR_OPEN.signal());
|
||||
let mut menu_open = use_signal(|| vec![false, false]);
|
||||
|
||||
let highlight_class = |e: &str| {
|
||||
match e == route_name {
|
||||
true => "flex items-center px-6 py-2 mt-4 duration-200 border-l-4 bg-gray-600 bg-opacity-25 text-gray-100 border-gray-100",
|
||||
false => "flex items-center px-6 py-2 mt-4 duration-200 border-l-4 border-gray-900 text-gray-500 hover:bg-gray-600 hover:bg-opacity-25 hover:text-gray-100",
|
||||
}
|
||||
};
|
||||
let menus = use_hook(|| MENUS.signal());
|
||||
|
||||
let toggle_sidebar = if sidebar_open() {
|
||||
"translate-x-0 ease-out"
|
||||
@@ -50,67 +44,8 @@ pub fn view() -> Element {
|
||||
}
|
||||
}
|
||||
// menu
|
||||
nav { class: "mt-10 ",
|
||||
Link { class: highlight_class("dashboard"), to: "/dashboard",
|
||||
icons::icon_chart {}
|
||||
span { class: "mx-4", "Dashboard" }
|
||||
}
|
||||
Link { class: highlight_class("ui-elements"), to: "/ui-elements",
|
||||
icons::icon_win {}
|
||||
span { class: "mx-4", "UI Elements" }
|
||||
}
|
||||
Link { class: highlight_class("tables"), to: "/tables",
|
||||
icons::icon_table {}
|
||||
span { class: "mx-4", "Tables" }
|
||||
}
|
||||
Link { class: highlight_class("forms"), to: "/forms",
|
||||
icons::icon_form {}
|
||||
span { class: "mx-4", "Forms" }
|
||||
}
|
||||
Link { class: highlight_class("cards"), to: "/cards",
|
||||
icons::icon_card {}
|
||||
span { class: "mx-4", "Cards" }
|
||||
}
|
||||
Link { class: highlight_class("modal"), to: "/modal",
|
||||
icons::icon_model {}
|
||||
span { class: "mx-4", "Modal" }
|
||||
}
|
||||
{menu::View(menus())}
|
||||
|
||||
Link { class: highlight_class("blank"), to: "/blank",
|
||||
icons::icon_blank {}
|
||||
span { class: "mx-4", "Blank" }
|
||||
}
|
||||
|
||||
// ul{li{}}
|
||||
div {
|
||||
div {
|
||||
class: "flex items-center px-6 py-2 mt-4 duration-200 border-l-4 border-gray-900 text-gray-500 hover:bg-gray-600 hover:bg-opacity-25 hover:text-gray-100 cursor-pointer",
|
||||
onclick: move |_| {
|
||||
let mut is_menu_open = menu_open.write();
|
||||
is_menu_open[0] = !is_menu_open[0];
|
||||
},
|
||||
|
||||
div { class: "flex items-center space-x-4",
|
||||
icons::icon_chart {}
|
||||
span { "Test" }
|
||||
div { class: format!("{}", if menu_open()[0] { "rotate-180" } else { "" }),
|
||||
icons::icon_up_down {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div { class: format!("ml-8 mt-1 {}", if menu_open()[0] { "block" } else { "hidden" }),
|
||||
Link { class: highlight_class("blank"), to: "/blank",
|
||||
icons::icon_blank {}
|
||||
span { class: "mx-4", "Blank" }
|
||||
}
|
||||
Link { class: highlight_class("blank2"), to: "/blank",
|
||||
icons::icon_blank {}
|
||||
span { class: "mx-4", "Blank" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -142,131 +77,4 @@ mod icons {
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
pub fn icon_chart() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2 10C2 5.58172 5.58172 2 10 2V10H18C18 14.4183 14.4183 18 10 18C5.58172 18 2 14.4183 2 10Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M12 2.25195C14.8113 2.97552 17.0245 5.18877 17.748 8.00004H12V2.25195Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_win() -> Element {
|
||||
html!(
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M5 3C3.89543 3 3 3.89543 3 5V7C3 8.10457 3.89543 9 5 9H7C8.10457 9 9 8.10457 9 7V5C9 3.89543 8.10457 3 7 3H5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M5 11C3.89543 11 3 11.8954 3 13V15C3 16.1046 3.89543 17 5 17H7C8.10457 17 9 16.1046 9 15V13C9 11.8954 8.10457 11 7 11H5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11 5C11 3.89543 11.8954 3 13 3H15C16.1046 3 17 3.89543 17 5V7C17 8.10457 16.1046 9 15 9H13C11.8954 9 11 8.10457 11 7V5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M11 13C11 11.8954 11.8954 11 13 11H15C16.1046 11 17 11.8954 17 13V15C17 16.1046 16.1046 17 15 17H13C11.8954 17 11 16.1046 11 15V13Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_table() -> Element {
|
||||
html!(
|
||||
<svg
|
||||
class="w-5 h-5"
|
||||
view_box="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M7 3C6.44772 3 6 3.44772 6 4C6 4.55228 6.44772 5 7 5H13C13.5523 5 14 4.55228 14 4C14 3.44772 13.5523 3 13 3H7Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M4 7C4 6.44772 4.44772 6 5 6H15C15.5523 6 16 6.44772 16 7C16 7.55228 15.5523 8 15 8H5C4.44772 8 4 7.55228 4 7Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2 11C2 9.89543 2.89543 9 4 9H16C17.1046 9 18 9.89543 18 11V15C18 16.1046 17.1046 17 16 17H4C2.89543 17 2 16.1046 2 15V11Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_form() -> Element {
|
||||
html!(
|
||||
<svg class="w-5 h-5" fill="currentColor" view_box="0 0 20 20">
|
||||
<path
|
||||
d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z"
|
||||
/>
|
||||
<path
|
||||
fill_rule="evenodd"
|
||||
d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z"
|
||||
clip_rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_card() -> Element {
|
||||
html!(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" view_box="0 0 20 20" fill="currentColor">
|
||||
<path d="M4 4a2 2 0 00-2 2v1h16V6a2 2 0 00-2-2H4z" />
|
||||
<path fill_rule="evenodd" d="M18 9H2v5a2 2 0 002 2h12a2 2 0 002-2V9zM4 13a1 1 0 011-1h1a1 1 0 110 2H5a1 1 0 01-1-1zm5-1a1 1 0 100 2h1a1 1 0 100-2H9z"
|
||||
clip_rule="evenodd" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_model() -> Element {
|
||||
html!(
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" view_box="0 0 20 20" fill="currentColor">
|
||||
<path d="M3 12v3c0 1.657 3.134 3 7 3s7-1.343 7-3v-3c0 1.657-3.134 3-7 3s-7-1.343-7-3z" />
|
||||
<path d="M3 7v3c0 1.657 3.134 3 7 3s7-1.343 7-3V7c0 1.657-3.134 3-7 3S3 8.657 3 7z" />
|
||||
<path d="M17 5c0 1.657-3.134 3-7 3S3 6.657 3 5s3.134-3 7-3 7 1.343 7 3z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
pub fn icon_blank() -> Element {
|
||||
html!(
|
||||
<svg class="w-5 h-5" fill="currentColor" view_box="0 0 20 20">
|
||||
<path
|
||||
d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
// #[inline_props]
|
||||
pub fn icon_up_down() -> Element {
|
||||
html!(
|
||||
<svg class="w-3 h-3"
|
||||
fill="currentColor"
|
||||
view_box="0 0 12 12">
|
||||
<path d="M5.9 11.4L.5 6l1.4-1.4 4 4 4-4L11.3 6z"></path>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* @Date: 2022-10-11 00:07:45
|
||||
* @LastEditTime: 2024-07-05 18:29:21
|
||||
* @LastEditTime: 2024-07-08 23:28:44
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/*
|
||||
* @Date: 2022-10-10 23:58:17
|
||||
* @LastEditTime: 2024-07-06 18:52:26
|
||||
* @LastEditTime: 2024-07-08 23:28:50
|
||||
* @Description:
|
||||
*/
|
||||
pub mod app;
|
||||
pub mod components;
|
||||
pub mod modules;
|
||||
pub mod utils;
|
||||
pub mod views;
|
||||
|
||||
use app::App;
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
/*
|
||||
* @Date: 2022-10-14 18:11:55
|
||||
* @LastEditTime: 2024-07-05 12:38:05
|
||||
* @LastEditTime: 2024-07-10 22:53:56
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
use dioxus::signals::{GlobalSignal, Signal};
|
||||
|
||||
use crate::components::{
|
||||
menu::icons,
|
||||
menu::{build_tree, MenuItem},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct UseTableData {
|
||||
pub simpleTableData: Vec<SimpleTableData>,
|
||||
pub paginatedTableData: Vec<PaginatedTableData>,
|
||||
pub wideTableData: Vec<WideTableData>,
|
||||
}
|
||||
|
||||
/// 表格测试数据 for tables
|
||||
@@ -65,16 +69,109 @@ pub static USE_TABLE_DATA: GlobalSignal<UseTableData> = Signal::global(|| {
|
||||
status: "Inactive".to_string(),
|
||||
statusColor: "red".to_string(),
|
||||
},
|
||||
],
|
||||
wideTableData: (0..5).map(|_i| {
|
||||
WideTableData {
|
||||
name: "John Doe".into(),
|
||||
email: "john@example.com".into(),
|
||||
title: "Software Engineer".into(),
|
||||
title2: "Web dev".into(),
|
||||
status: "Active".into(),
|
||||
role: "Owner".into(),
|
||||
}
|
||||
}).collect(),
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// test data
|
||||
pub static USERS: GlobalSignal<Vec<User>> = Signal::global(|| {
|
||||
(0..5)
|
||||
.map(|_i| User {
|
||||
name: "John Doe".into(),
|
||||
email: "john@example.com".into(),
|
||||
title: "Software Engineer".into(),
|
||||
title2: "Web dev".into(),
|
||||
status: if _i % 2 == 0 { "Active" } else { "Inactive" }.into(),
|
||||
role: "Owner".into(),
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
// test data
|
||||
pub static MENUS: GlobalSignal<Vec<MenuItem>> = Signal::global(|| {
|
||||
let data = vec![
|
||||
MenuItem {
|
||||
id: 1,
|
||||
key: "/dashboard".to_string(),
|
||||
label: "Dashboard".to_string(),
|
||||
// icon: "icon_chart".to_string(),
|
||||
icon: icons::icon_chart(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 2,
|
||||
key: "/ui-elements".to_string(),
|
||||
label: "UI Elements".to_string(),
|
||||
// icon: "icon_element".to_string(),
|
||||
icon: icons::icon_element(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 3,
|
||||
key: "/tables".to_string(),
|
||||
label: "Tables".to_string(),
|
||||
// icon: "icon_table".to_string(),
|
||||
icon: icons::icon_table(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 4,
|
||||
key: "/forms".to_string(),
|
||||
label: "Forms".to_string(),
|
||||
// icon: "icon_form".to_string(),
|
||||
icon: icons::icon_form(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 5,
|
||||
key: "/cards".to_string(),
|
||||
label: "Cards".to_string(),
|
||||
// icon: "icon_card".to_string(),
|
||||
icon: icons::icon_card(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 6,
|
||||
key: "/modal".to_string(),
|
||||
label: "Modal".to_string(),
|
||||
// icon: "icon_model".to_string(),
|
||||
icon: icons::icon_model(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 7,
|
||||
key: "#".to_string(),
|
||||
label: "Test".to_string(),
|
||||
// icon: "icon_element".to_string(),
|
||||
icon: icons::icon_element(),
|
||||
parent_id: 0,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 8,
|
||||
key: "/blank1".to_string(),
|
||||
label: "Blank".to_string(),
|
||||
// icon: "icon_blank".to_string(),
|
||||
icon: icons::icon_blank(),
|
||||
parent_id: 7,
|
||||
children: vec![],
|
||||
},
|
||||
MenuItem {
|
||||
id: 9,
|
||||
key: "/blank2".to_string(),
|
||||
label: "Blank".to_string(),
|
||||
// icon: "icon_blank".to_string(),
|
||||
icon: icons::icon_blank(),
|
||||
parent_id: 7,
|
||||
children: vec![],
|
||||
},
|
||||
];
|
||||
|
||||
build_tree(data, 0)
|
||||
});
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* @Date: 2024-07-06 00:18:55
|
||||
* @LastEditTime: 2024-07-09 11:37:47
|
||||
*/
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
pub mod demo_data;
|
||||
@@ -28,12 +33,3 @@ pub struct PaginatedTableData {
|
||||
pub status: String,
|
||||
pub statusColor: String,
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct WideTableData {
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
pub title: String,
|
||||
pub title2: String,
|
||||
pub status: String,
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
2
src/utils/mod.rs
Normal file
2
src/utils/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
pub mod time;
|
||||
21
src/utils/time.rs
Normal file
21
src/utils/time.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* @Date: 2024-07-08 23:25:28
|
||||
* @LastEditTime: 2024-07-09 11:19:51
|
||||
*/
|
||||
#![allow(unused_imports)]
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use async_std::task::sleep;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use instant::{Duration, Instant};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::sleep;
|
||||
|
||||
pub async fn sleep_ms(ms: u64) {
|
||||
sleep(Duration::from_millis(ms)).await;
|
||||
}
|
||||
|
||||
pub async fn instant() -> Instant {
|
||||
Instant::now()
|
||||
}
|
||||
26
src/views/chart.rs
Normal file
26
src/views/chart.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* @Date: 2024-07-08 16:13:27
|
||||
* @LastEditTime: 2024-07-11 09:09:54
|
||||
*/
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::utils::time::sleep_ms;
|
||||
|
||||
pub fn eval_chart() {
|
||||
eval_chart1();
|
||||
eval_chart2();
|
||||
}
|
||||
pub fn eval_chart1() {
|
||||
let script = include_str!("js/chart1.js");
|
||||
let _eval = eval(script);
|
||||
|
||||
spawn(async move {
|
||||
sleep_ms(1000).await;
|
||||
_eval.send((vec![148, 150, 130, 170]).into()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn eval_chart2() {
|
||||
let script = include_str!("js/chart2.js");
|
||||
let _eval = eval(script);
|
||||
}
|
||||
@@ -1,29 +1,17 @@
|
||||
/*
|
||||
* @Date: 2022-10-14 12:31:43
|
||||
* @LastEditTime: 2024-07-05 12:56:53
|
||||
* @LastEditTime: 2024-07-11 09:10:07
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::modules::User;
|
||||
|
||||
// test data
|
||||
pub static USERS: GlobalSignal<Vec<User>> = Signal::global(|| {
|
||||
(0..5)
|
||||
.map(|_i| User {
|
||||
name: "John Doe".into(),
|
||||
email: "john@example.com".into(),
|
||||
title: "Software Engineer".into(),
|
||||
title2: "Web dev".into(),
|
||||
status: "Active".into(),
|
||||
role: "Owner".into(),
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
use crate::views::chart::*;
|
||||
|
||||
pub fn view() -> Element {
|
||||
let users = use_hook(|| USERS.signal());
|
||||
spawn(async move {
|
||||
eval_chart();
|
||||
});
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
@@ -33,7 +21,7 @@ pub fn view() -> Element {
|
||||
div { class: "w-full px-6 sm:w-1/2 xl:w-1/3",
|
||||
div { class: "flex items-center px-5 py-6 bg-white rounded-md shadow-sm",
|
||||
div { class: "p-3 bg-indigo-600 bg-opacity-75 rounded-full",
|
||||
icons::icon_1 {}
|
||||
icons::icon_users {}
|
||||
}
|
||||
div { class: "mx-5",
|
||||
h4 { class: "text-2xl font-semibold text-gray-700",
|
||||
@@ -46,7 +34,7 @@ pub fn view() -> Element {
|
||||
div { class: "w-full px-6 mt-6 sm:w-1/2 xl:w-1/3 sm:mt-0",
|
||||
div { class: "flex items-center px-5 py-6 bg-white rounded-md shadow-sm",
|
||||
div { class: "p-3 bg-blue-600 bg-opacity-75 rounded-full",
|
||||
icons::icon_2 {}
|
||||
icons::icon_go {}
|
||||
}
|
||||
div { class: "mx-5",
|
||||
h4 { class: "text-2xl font-semibold text-gray-700",
|
||||
@@ -59,7 +47,7 @@ pub fn view() -> Element {
|
||||
div { class: "w-full px-6 mt-6 sm:w-1/2 xl:w-1/3 xl:mt-0",
|
||||
div { class: "flex items-center px-5 py-6 bg-white rounded-md shadow-sm",
|
||||
div { class: "p-3 bg-pink-600 bg-opacity-75 rounded-full",
|
||||
icons::icon_3 {}
|
||||
icons::icon_products {}
|
||||
}
|
||||
div { class: "mx-5",
|
||||
h4 { class: "text-2xl font-semibold text-gray-700",
|
||||
@@ -73,86 +61,25 @@ pub fn view() -> Element {
|
||||
}
|
||||
div { class: "mt-8" }
|
||||
|
||||
div { class: "flex flex-col mt-8",
|
||||
div { class: "py-2 -my-2 overflow-x-auto sm:-mx-6 sm:px-6 lg:-mx-8 lg:px-8",
|
||||
div { class: "inline-block min-w-full overflow-hidden align-middle border-b border-gray-200 shadow sm:rounded-lg",
|
||||
table { class: "min-w-full",
|
||||
thead {
|
||||
tr {
|
||||
th { class: "px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-50",
|
||||
"Name"
|
||||
}
|
||||
th { class: "px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-50",
|
||||
"Title"
|
||||
}
|
||||
th { class: "px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-50",
|
||||
"Status"
|
||||
}
|
||||
th { class: "px-6 py-3 text-xs font-medium leading-4 tracking-wider text-left text-gray-500 uppercase border-b border-gray-200 bg-gray-50",
|
||||
"Role"
|
||||
}
|
||||
th { class: "px-6 py-3 border-b border-gray-200 bg-gray-50" }
|
||||
}
|
||||
}
|
||||
// 表格数据
|
||||
tbody { class: "bg-white",
|
||||
{users}.iter().map(|u|{
|
||||
rsx!{ UserList{user:u.clone()}}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(PartialEq, Clone, Props)]
|
||||
// pub struct UserListProps {
|
||||
// user: User,
|
||||
// }
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
pub fn UserList(user: User) -> Element {
|
||||
// let u = cx.props.user;
|
||||
let u = user;
|
||||
rsx! {
|
||||
tr {
|
||||
// key: "{index}",
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div { class: "flex items-center",
|
||||
div { class: "flex-shrink-0 w-10 h-10",
|
||||
img {
|
||||
class: "w-10 h-10 rounded-full",
|
||||
alt: "",
|
||||
src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
}
|
||||
div { class: "flex flex-wrap mt-6",
|
||||
div { class: "w-full lg:w-1/2 pr-0 lg:pr-2",
|
||||
p { class: "text-xl pb-3 flex items-center",
|
||||
"Monthly Reports"
|
||||
}
|
||||
div { class: "ml-4",
|
||||
div { class: "text-sm font-medium leading-5 text-gray-900",
|
||||
"{ u.name }"
|
||||
}
|
||||
div { class: "text-sm leading-5 text-gray-500", "{ u.email }" }
|
||||
div { class: "p-6 bg-white",
|
||||
canvas {id: "chart1" }
|
||||
}
|
||||
}
|
||||
}
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div { class: "text-sm leading-5 text-gray-900", "{ u.title }" }
|
||||
div { class: "text-sm leading-5 text-gray-500", "{ u.title2 }" }
|
||||
}
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
span { class: "inline-flex px-2 text-xs font-semibold leading-5 text-green-800 bg-green-100 rounded-full",
|
||||
"{ u.status }"
|
||||
div { class: "w-full lg:w-1/2 pl-0 lg:pl-2 mt-6 lg:mt-0",
|
||||
p { class: "text-xl pb-3 flex items-center",
|
||||
"Resolved Reports"
|
||||
}
|
||||
div { class: "p-6 bg-white",
|
||||
canvas { id: "chart2" }
|
||||
}
|
||||
}
|
||||
}
|
||||
td { class: "px-6 py-4 text-sm leading-5 text-gray-500 border-b border-gray-200 whitespace-nowrap",
|
||||
"{ u.role }"
|
||||
}
|
||||
td { class: "px-6 py-4 text-sm font-medium leading-5 text-right border-b border-gray-200 whitespace-nowrap",
|
||||
a { class: "text-indigo-600 hover:text-indigo-900", href: "#", "Edit" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +88,7 @@ mod icons {
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html_macro::html;
|
||||
|
||||
pub fn icon_1() -> Element {
|
||||
pub fn icon_users() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-8 h-8 text-white"
|
||||
@@ -197,7 +124,7 @@ mod icons {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_2() -> Element {
|
||||
pub fn icon_go() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-8 h-8 text-white"
|
||||
@@ -221,7 +148,7 @@ mod icons {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_3() -> Element {
|
||||
pub fn icon_products() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-8 h-8 text-white"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* @Date: 2022-10-15 00:32:59
|
||||
* @LastEditTime: 2024-07-05 20:21:11
|
||||
* @LastEditTime: 2024-07-11 10:47:34
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,7 @@ struct User {
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
pub confirm: String,
|
||||
// pub confirm: String,
|
||||
}
|
||||
|
||||
pub fn view() -> Element {
|
||||
@@ -64,7 +64,8 @@ fn Model_form() -> Element {
|
||||
class: "px-3 py-1 text-sm text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300 focus:outline-none",
|
||||
"Cancel"
|
||||
}
|
||||
button { class: "px-3 py-1 text-sm text-white bg-indigo-600 rounded-md hover:bg-indigo-500 focus:outline-none",
|
||||
button {
|
||||
class: "px-3 py-1 text-sm text-white bg-indigo-600 rounded-md hover:bg-indigo-500 focus:outline-none",
|
||||
"Save"
|
||||
}
|
||||
}
|
||||
@@ -78,14 +79,10 @@ fn Model_form() -> Element {
|
||||
// Forms
|
||||
fn Forms() -> Element {
|
||||
let mut user = use_signal(User::default);
|
||||
let ur = user.peek();
|
||||
// let User {
|
||||
// username,
|
||||
// email,
|
||||
// password,
|
||||
// confirm,
|
||||
// ..
|
||||
// } = &*user.read();
|
||||
let u = user.peek();
|
||||
let mut confirm = use_signal(|| "".to_string());
|
||||
|
||||
let mut errors = use_signal(|| vec![None::<String>; 4]);
|
||||
|
||||
rsx! {
|
||||
div { class: "mt-8",
|
||||
@@ -95,11 +92,23 @@ fn Forms() -> Element {
|
||||
h2 { class: "text-lg font-semibold text-gray-700 capitalize", "Account settings" }
|
||||
|
||||
form {
|
||||
//action="" methods="post"
|
||||
prevent_default: "onsubmit",
|
||||
onsubmit: move |e| {
|
||||
info!("onsubmit: {:?}", e);
|
||||
info!("onsubmit: {:?}", user.peek());
|
||||
// prevent_default: "onsubmit",
|
||||
onsubmit: move |_e| {
|
||||
info!("onsubmit: {:?}", user);
|
||||
let u = user.read();
|
||||
let mut errors = errors.write();
|
||||
errors[0] = if u.username.is_empty() { Some("Username is required".to_string()) } else { None };
|
||||
errors[1] = if u.email.is_empty() { Some("Email is required".to_string()) } else { None };
|
||||
errors[2] = if u.password.is_empty() { Some("Password is required".to_string()) } else { None };
|
||||
errors[3] = if confirm().is_empty() { Some("Confirm is required".to_string()) } else { None };
|
||||
|
||||
for e in errors.iter() {
|
||||
if e.is_some() {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
tracing::info!("onsubmit ok");
|
||||
},
|
||||
|
||||
div { class: "grid grid-cols-1 gap-6 mt-4 sm:grid-cols-2",
|
||||
@@ -109,15 +118,18 @@ fn Forms() -> Element {
|
||||
id: "username",
|
||||
class: "w-full mt-2 c-input",
|
||||
r#type: "text",
|
||||
// "v-model: "user.username",
|
||||
// 双向绑定
|
||||
value: "{ur.username}",
|
||||
value: "{u.username}",
|
||||
oninput: {
|
||||
move |e| {
|
||||
user.write().username = e.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
{errors()[0].as_ref().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
div {
|
||||
label { class: "text-gray-700", r#for: "email", "Email Address" }
|
||||
@@ -125,13 +137,18 @@ fn Forms() -> Element {
|
||||
id: "email",
|
||||
class: "w-full mt-2 c-input",
|
||||
r#type: "email",
|
||||
value: "{ur.email}",
|
||||
value: "{u.email}",
|
||||
oninput: {
|
||||
move |e| {
|
||||
user.write().email = e.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
{errors()[1].as_ref().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
div {
|
||||
label { class: "text-gray-700", r#for: "password", "Password" }
|
||||
@@ -139,13 +156,18 @@ fn Forms() -> Element {
|
||||
id: "password",
|
||||
class: "w-full mt-2 c-input",
|
||||
r#type: "password",
|
||||
value: "{ur.password}",
|
||||
value: "{u.password}",
|
||||
oninput: {
|
||||
move |e| {
|
||||
user.write().password = e.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
{errors()[2].as_ref().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
div {
|
||||
label {
|
||||
@@ -157,13 +179,18 @@ fn Forms() -> Element {
|
||||
id: "pwConfirm",
|
||||
class: "w-full mt-2 c-input",
|
||||
r#type: "password",
|
||||
value: "{ur.confirm}",
|
||||
value: "{confirm}",
|
||||
oninput: {
|
||||
move |e| {
|
||||
user.write().confirm = e.value();
|
||||
confirm.set(e.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
{errors()[3].as_ref().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
}
|
||||
div { class: "flex justify-end mt-4",
|
||||
|
||||
49
src/views/js/chart1.js
Normal file
49
src/views/js/chart1.js
Normal file
@@ -0,0 +1,49 @@
|
||||
const eval_chart1 = (function () {
|
||||
const ctx = document.getElementById('chart1');
|
||||
let chartdata = [148, 150, 130, 170];
|
||||
const data = {
|
||||
labels: [
|
||||
'Food & beverages',
|
||||
'Groceries',
|
||||
'Gaming',
|
||||
'Trip & holiday',
|
||||
],
|
||||
datasets: [{
|
||||
label: 'Total Expenses',
|
||||
data: chartdata,
|
||||
backgroundColor: [
|
||||
'#3B82F6',
|
||||
'#10B981',
|
||||
'#6366F1',
|
||||
'#F59E0B'
|
||||
]
|
||||
}]
|
||||
};
|
||||
const config = {
|
||||
type: 'bar', //polarArea, bar, line, doughnut, pie, radar, scatter
|
||||
data: data,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
const chart = new Chart(ctx, config);
|
||||
// 接收 Rust 发送的数据并更新图表
|
||||
(async function () {
|
||||
while (true) {
|
||||
const msg = await dioxus.recv();
|
||||
// console.log(msg);
|
||||
chart.data.datasets[0].data = msg;
|
||||
chart.update();
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
eval_chart1();
|
||||
}, 100);
|
||||
56
src/views/js/chart2.js
Normal file
56
src/views/js/chart2.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const eval_chart2 = (function () {
|
||||
const ctx = document.getElementById('chart2');
|
||||
let chartdata = [12, 19, 3, 5, 2, 3];
|
||||
const data = {
|
||||
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
|
||||
datasets: [{
|
||||
label: '# of Votes',
|
||||
data: chartdata,
|
||||
backgroundColor: [
|
||||
'rgba(255, 99, 132, 0.2)',
|
||||
'rgba(54, 162, 235, 0.2)',
|
||||
'rgba(255, 206, 86, 0.2)',
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(153, 102, 255, 0.2)',
|
||||
'rgba(255, 159, 64, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgba(255, 99, 132, 1)',
|
||||
'rgba(54, 162, 235, 1)',
|
||||
'rgba(255, 206, 86, 1)',
|
||||
'rgba(75, 192, 192, 1)',
|
||||
'rgba(153, 102, 255, 1)',
|
||||
'rgba(255, 159, 64, 1)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
};
|
||||
const config = {
|
||||
type: 'line',
|
||||
data: data,
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
const chart = new Chart(ctx, config);
|
||||
|
||||
(async function () {
|
||||
while (true) {
|
||||
const msg = await dioxus.recv();
|
||||
// console.log(msg);
|
||||
chart.data.datasets[0].data = msg;
|
||||
chart.update();
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
eval_chart2();
|
||||
}, 100);
|
||||
0
src/views/js/mod.rs
Normal file
0
src/views/js/mod.rs
Normal file
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* @Date: 2022-10-12 00:00:54
|
||||
* @LastEditTime: 2024-07-05 20:06:58
|
||||
* @LastEditTime: 2024-07-11 09:54:59
|
||||
* @Description:
|
||||
*/
|
||||
use dioxus::prelude::*;
|
||||
@@ -8,6 +8,8 @@ use dioxus::prelude::*;
|
||||
pub fn view() -> Element {
|
||||
let mut email = use_signal(|| "".to_string());
|
||||
let mut password = use_signal(|| "".to_string());
|
||||
let mut email_error = use_signal(|| None::<String>);
|
||||
let mut password_error = use_signal(|| None::<String>);
|
||||
|
||||
fn login() {
|
||||
let router = router();
|
||||
@@ -25,6 +27,11 @@ pub fn view() -> Element {
|
||||
form {
|
||||
class: "mt-4",
|
||||
onsubmit: move |_| {
|
||||
email_error.set(if email().is_empty() { Some("Email is required".to_string()) } else { None });
|
||||
password_error.set(if password().is_empty() { Some("Password is required".to_string())} else {None});
|
||||
if email().is_empty() || password().is_empty() {
|
||||
// return;
|
||||
}
|
||||
login();
|
||||
},
|
||||
label { class: "block",
|
||||
@@ -38,6 +45,11 @@ pub fn view() -> Element {
|
||||
email.set(e.value());
|
||||
}
|
||||
}
|
||||
{email_error().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
label { class: "block mt-3",
|
||||
span { class: "text-sm text-gray-700", "Password" }
|
||||
@@ -50,6 +62,11 @@ pub fn view() -> Element {
|
||||
password.set(e.value());
|
||||
}
|
||||
}
|
||||
{password_error().map(|error| {
|
||||
rsx! {
|
||||
p { class: "mt-2 text-sm text-red-600", "{error}" }
|
||||
}
|
||||
})}
|
||||
}
|
||||
div { class: "flex items-center justify-between mt-4",
|
||||
div {
|
||||
|
||||
@@ -1,23 +1,16 @@
|
||||
/*
|
||||
|
||||
* @Date: 2022-10-11 23:13:24
|
||||
* @LastEditTime: 2022-10-15 09:53:40
|
||||
* @Description:
|
||||
*/
|
||||
* @Date: 2022-10-11 23:13:24
|
||||
* @LastEditTime: 2024-07-09 11:06:42
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
pub mod blank;
|
||||
pub mod card;
|
||||
pub mod chart;
|
||||
pub mod dashboard;
|
||||
pub mod forms;
|
||||
pub mod login;
|
||||
pub mod modal;
|
||||
pub mod tables;
|
||||
pub mod ui_elements;
|
||||
|
||||
// pub fn view()->Element{
|
||||
|
||||
// (rsx!{
|
||||
// div {
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* @Date: 2022-10-15 09:52:14
|
||||
* @LastEditTime: 2024-07-07 21:21:12
|
||||
* @LastEditTime: 2024-07-09 16:42:32
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
@@ -26,7 +26,7 @@ pub fn view() -> Element {
|
||||
div {
|
||||
class: format!(
|
||||
"transition:opacity 0.25s ease {} z-50 fixed w-full h-full top-0 left-0 flex items-center justify-center",
|
||||
if open() { "false" } else { "opacity-0 pointer-events-none" },
|
||||
if open() { "" } else { "opacity-0 pointer-events-none" },
|
||||
),
|
||||
// overlay
|
||||
div { class: "absolute w-full h-full bg-gray-900 opacity-50 modal-overlay" }
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/*
|
||||
* @Date: 2022-10-14 17:43:39
|
||||
* @LastEditTime: 2024-07-07 21:20:43
|
||||
* @LastEditTime: 2024-07-09 16:38:14
|
||||
* @Description:
|
||||
*/
|
||||
#![allow(non_snake_case)]
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::modules::demo_data::USE_TABLE_DATA;
|
||||
use crate::modules::{
|
||||
demo_data::{USERS, USE_TABLE_DATA},
|
||||
User,
|
||||
};
|
||||
|
||||
pub fn view() -> Element {
|
||||
rsx! {
|
||||
@@ -71,16 +74,15 @@ fn Simple_table() -> Element {
|
||||
}
|
||||
|
||||
// 分页 table
|
||||
// Table with pagination
|
||||
fn Table_with_pagination() -> Element {
|
||||
let table_data = use_hook(|| USE_TABLE_DATA.signal());
|
||||
let paginated_table_data = &table_data.read().paginatedTableData;
|
||||
|
||||
let status_color = |status: &str| match status {
|
||||
"Active" => ("bg-green-100", "text-green-800"),
|
||||
"Inactive" => ("bg-red-100", "text-red-800"),
|
||||
"Suspended" => ("bg-orange-100", "text-orange-800"),
|
||||
_ => ("bg-gray-100", "text-gray-800"),
|
||||
"Active" => "bg-green-100 text-green-800",
|
||||
"Inactive" => "bg-red-100 text-red-800",
|
||||
"Suspended" => "bg-orange-100 text-orange-800",
|
||||
_ => "bg-gray-100 text-gray-800",
|
||||
};
|
||||
|
||||
rsx! {
|
||||
@@ -97,25 +99,21 @@ fn Table_with_pagination() -> Element {
|
||||
option { "10" }
|
||||
option { "20" }
|
||||
}
|
||||
div { class: "absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 pointer-events-none",
|
||||
icons::icon_1 {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
div { class: "relative",
|
||||
select { class: "block w-full h-full px-4 py-2 pr-8 leading-tight text-gray-700 bg-white border-t border-b border-r border-gray-400 rounded-r appearance-none sm:rounded-r-none sm:border-r-0 focus:outline-none focus:border-l focus:border-r focus:bg-white focus:border-gray-500",
|
||||
select { class: "block w-full h-full px-4 py-2 pr-8 leading-tight text-gray-700 bg-white border border-gray-400 rounded-r appearance-none sm:rounded-r-none sm:border-r-0 focus:outline-none focus:border-l focus:border-r focus:bg-white focus:border-gray-500",
|
||||
option { "All" }
|
||||
option { "Active" }
|
||||
option { "Inactive" }
|
||||
}
|
||||
div { class: "absolute inset-y-0 right-0 flex items-center px-2 text-gray-700 pointer-events-none",
|
||||
icons::icon_2 {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
div { class: "relative block mt-2 sm:mt-0",
|
||||
span { class: "absolute inset-y-0 left-0 flex items-center pl-2",
|
||||
icons::icon_3 {}
|
||||
icons::icon_search {}
|
||||
}
|
||||
input {
|
||||
class: "block w-full py-2 pl-8 pr-6 text-sm text-gray-700 placeholder-gray-400 bg-white border border-b border-gray-400 rounded-l rounded-r appearance-none sm:rounded-l-none focus:bg-white focus:placeholder-gray-600 focus:text-gray-700 focus:outline-none",
|
||||
@@ -188,26 +186,10 @@ fn Table_with_pagination() -> Element {
|
||||
td {
|
||||
class: "px-5 py-5 text-sm bg-white border-b border-gray-200",
|
||||
span {
|
||||
class: format!("inline-flex px-3 py-1 font-semibold leading-tight rounded-full {} {}",status_color(&u.status).0,status_color(&u.status).1),
|
||||
class: "inline-flex px-3 py-1 font-semibold leading-tight rounded-full { status_color(&u.status)} ",
|
||||
"{ u.status }"
|
||||
}
|
||||
|
||||
// span {
|
||||
// class: format!("relative inline-block px-3 py-1 font-semibold leading-tight {}", status_color(&u.status).1),
|
||||
// span {
|
||||
// aria_hidden: "true",
|
||||
// class: {
|
||||
// format!("absolute inset-0 opacity-50 rounded-full {}", status_color(&u.status).0)
|
||||
// }
|
||||
// }
|
||||
// span {
|
||||
// class: "relative",
|
||||
// "{u.status}"
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
//tr end
|
||||
}
|
||||
@@ -238,8 +220,8 @@ fn Table_with_pagination() -> Element {
|
||||
|
||||
// 宽表格
|
||||
fn Wide_table() -> Element {
|
||||
let table_data = use_hook(|| USE_TABLE_DATA.signal());
|
||||
let wide_table_data = &table_data.read().wideTableData;
|
||||
let users = use_hook(|| USERS.signal());
|
||||
|
||||
rsx! {
|
||||
div { class: "mt-8",
|
||||
h4 { class: "text-gray-600", "Wide Table" }
|
||||
@@ -264,68 +246,11 @@ fn Wide_table() -> Element {
|
||||
th { class: "px-6 py-3 bg-gray-100 border-b border-gray-200" }
|
||||
}
|
||||
}
|
||||
// data
|
||||
// 表格数据
|
||||
tbody { class: "bg-white",
|
||||
// iter start
|
||||
{wide_table_data}.iter().map(|u|{rsx!{tr{
|
||||
|
||||
td {
|
||||
class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div {
|
||||
class: "flex items-center",
|
||||
div {
|
||||
class: "flex-shrink-0 w-10 h-10",
|
||||
img {
|
||||
class: "w-10 h-10 rounded-full",
|
||||
alt: "profile pic",
|
||||
src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
|
||||
}
|
||||
}
|
||||
div {
|
||||
class: "ml-4",
|
||||
div {
|
||||
class: "text-sm font-medium leading-5 text-gray-900",
|
||||
"{u.name}"
|
||||
}
|
||||
div {
|
||||
class: "text-sm leading-5 text-gray-500",
|
||||
"{ u.email }"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
td {
|
||||
class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div {
|
||||
class: "text-sm leading-5 text-gray-900",
|
||||
"{ u.title }"
|
||||
}
|
||||
div {
|
||||
class: "text-sm leading-5 text-gray-500",
|
||||
"{ u.title2 }"
|
||||
}
|
||||
}
|
||||
td {
|
||||
class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
span {
|
||||
class: "inline-flex px-2 text-xs font-semibold leading-5 text-green-800 bg-green-100 rounded-full",
|
||||
"{ u.status }"
|
||||
}
|
||||
}
|
||||
td {
|
||||
class: "px-6 py-4 text-sm leading-5 text-gray-500 border-b border-gray-200 whitespace-nowrap",
|
||||
"{ u.role }"
|
||||
}
|
||||
td {
|
||||
class: "px-6 py-4 text-sm font-medium leading-5 text-right border-b border-gray-200 whitespace-nowrap",
|
||||
a {
|
||||
class: "text-indigo-600 hover:text-indigo-900",
|
||||
href: "#","Edit"
|
||||
}
|
||||
}
|
||||
|
||||
// iter end
|
||||
}}})
|
||||
{users}.iter().map(|u|{
|
||||
rsx!{ UserList{u: u.clone()}}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,25 +260,62 @@ fn Wide_table() -> Element {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[component]
|
||||
pub fn UserList(u: User) -> Element {
|
||||
// let u = cx.props.user;
|
||||
let status_color = |status: &str| match status {
|
||||
"Active" => "bg-green-100 text-green-800",
|
||||
"Inactive" => "bg-red-100 text-red-800",
|
||||
"Suspended" => "bg-orange-100 text-orange-800",
|
||||
_ => "bg-gray-100 text-gray-800",
|
||||
};
|
||||
|
||||
rsx! {
|
||||
tr {
|
||||
// key: "{index}",
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div { class: "flex items-center",
|
||||
div { class: "flex-shrink-0 w-10 h-10",
|
||||
img {
|
||||
class: "w-10 h-10 rounded-full",
|
||||
alt: "",
|
||||
src: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
|
||||
}
|
||||
}
|
||||
div { class: "ml-4",
|
||||
div { class: "text-sm font-medium leading-5 text-gray-900",
|
||||
"{ u.name }"
|
||||
}
|
||||
div { class: "text-sm leading-5 text-gray-500", "{ u.email }" }
|
||||
}
|
||||
}
|
||||
}
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
div { class: "text-sm leading-5 text-gray-900", "{ u.title }" }
|
||||
div { class: "text-sm leading-5 text-gray-500", "{ u.title2 }" }
|
||||
}
|
||||
td { class: "px-6 py-4 border-b border-gray-200 whitespace-nowrap",
|
||||
span { class: "inline-flex px-2 text-xs font-semibold leading-5 text-green-800 bg-green-100 rounded-full {status_color(&u.status)}",
|
||||
"{ u.status }"
|
||||
}
|
||||
}
|
||||
td { class: "px-6 py-4 text-sm leading-5 text-gray-500 border-b border-gray-200 whitespace-nowrap",
|
||||
"{ u.role }"
|
||||
}
|
||||
td { class: "px-6 py-4 text-sm font-medium leading-5 text-right border-b border-gray-200 whitespace-nowrap",
|
||||
a { class: "text-indigo-600 hover:text-indigo-900", href: "#", "Edit" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod icons {
|
||||
use dioxus::prelude::*;
|
||||
use dioxus_html_macro::html;
|
||||
|
||||
pub fn icon_1() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-4 h-4 fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
view_box="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z"
|
||||
/>
|
||||
</svg>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_2() -> Element {
|
||||
#[allow(unused)]
|
||||
pub fn icon_down() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
class="w-4 h-4 fill-current"
|
||||
@@ -367,7 +329,7 @@ mod icons {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_3() -> Element {
|
||||
pub fn icon_search() -> Element {
|
||||
html! {
|
||||
<svg
|
||||
view_box="0 0 24 24"
|
||||
|
||||
@@ -14,5 +14,7 @@ module.exports = {
|
||||
require('@tailwindcss/forms'),
|
||||
require('@tailwindcss/typography'),
|
||||
require('@tailwindcss/aspect-ratio'),
|
||||
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user