工作日倒数 v0.1.0

说明

学了应该快2个月的Rust,只看不动手,那肯定是不行的。所以写个小东西来实践一下。

同时发现坑也是真的多,Rust是真的难,代码还没有完成,也还有很多错误(又不是不能运行),还在持续学习。没有注释,有空再来说一下每个函数的作用。

还有雾化提醒没有加(开玩笑

成品放打赏区。可能会报毒,但放心使用!

上代码!

#![windows_subsystem = "windows"]

use chrono::{Datelike, Local, TimeZone};
use iced::theme::Theme;
use iced::widget::{column, container, pick_list, scrollable, text, vertical_space};
use iced::window::Position;
use iced::{Alignment, Application, Command, Element, Length, Settings};
use serde::{Deserialize, Serialize};
use std::fs;

const SETTINGS_FILE: &str = "settings.json";

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Eq)]
pub enum WeekName {
    SmallWeek,
    BigWeek,
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Eq)]
pub enum WorkingMode {
    Weekends,
    SingleDay,
    AutoSwitch(WeekName),
}

impl WorkingMode {
    const ALL: [WorkingMode; 4] = [
        WorkingMode::Weekends,
        WorkingMode::SingleDay,
        WorkingMode::AutoSwitch(WeekName::BigWeek),
        WorkingMode::AutoSwitch(WeekName::SmallWeek),
    ];
}

impl std::fmt::Display for WorkingMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                WorkingMode::Weekends => "双休",
                WorkingMode::SingleDay => "单休",
                WorkingMode::AutoSwitch(week_name) => {
                    if *week_name == WeekName::BigWeek {
                        "大小周(双休)"
                    } else {
                        "大小周(单休)"
                    }
                }
            }
        )
    }
}

impl Default for WorkingMode {
    fn default() -> Self {
        WorkingMode::Weekends
    }
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
struct SettingsData {
    first_time: bool,
    mode: WorkingMode,
    current_week: WorkingMode,
    last_update_date: chrono::DateTime<Local>,
}


struct Holiday {
    holiday_name: String,
    result_day: i32,
    need_day: u32,
    working_week: String,
    mode_picklist_state: Option<WorkingMode>,
    settings: SettingsData,
}

#[derive(Debug, Clone)]
enum Message {
    ModeSelected(WorkingMode),
}

impl Default for SettingsData {
    fn default() -> Self {
        let today = Local::now();
        SettingsData {
            mode: WorkingMode::default(),
            current_week: WorkingMode::default(),
            first_time: true,
            last_update_date: today,
        }
    }
}

impl SettingsData {
    fn load() -> Self {
        match fs::read_to_string(SETTINGS_FILE) {
            Ok(contents) => {
                if let Ok(data) = serde_json::from_str(&contents) {
                    data
                } else {
                    SettingsData::default()
                }
            }
            Err(_) => SettingsData::default(),
        }
    }

    fn save(&self) {
        if let Ok(data) = serde_json::to_string(self) {
            if let Err(err) = fs::write(SETTINGS_FILE, data) {
                eprintln!("保存失败: {}", err);
            }
        } else {
            eprintln!("序列化失败");
        }
    }
}

impl Holiday {
    fn is_monday(&self) -> bool {
        let today = Local::now();
        today.weekday() == chrono::Weekday::Mon
    }

    fn is_same_week(
        &self,
        date1: &chrono::DateTime<Local>,
        date2: &chrono::DateTime<Local>,
    ) -> bool {
        let duration = date1.signed_duration_since(*date2);
        duration.num_weeks() == 0
    }

    fn update_settings(&mut self, mode: WorkingMode) {
        let current_date = Local::now();
        let last_update_date = self.settings.last_update_date;
        self.settings.current_week = mode;
        self.settings.first_time = false;

        if !self.is_monday() && self.is_same_week(&current_date, &last_update_date) {
            self.settings.mode = mode;
            self.settings.save();
        } else {
            self.settings.mode = match mode {
                WorkingMode::AutoSwitch(WeekName::BigWeek) => {
                    self.settings.current_week = WorkingMode::AutoSwitch(WeekName::SmallWeek);
                    WorkingMode::AutoSwitch(WeekName::SmallWeek)
                }
                WorkingMode::AutoSwitch(WeekName::SmallWeek) => {
                    self.settings.current_week = WorkingMode::AutoSwitch(WeekName::BigWeek);
                    WorkingMode::AutoSwitch(WeekName::BigWeek)
                }

                _ => mode,
            };
            self.mode_picklist_state = Some(self.settings.mode);
            self.settings.last_update_date = Local::now();
            self.update_values();
            // self.settings.first_time = false;
            self.settings.save();
        }
    }

    fn update_values(&mut self) {
        let weekday = Local::now().weekday();
        self.working_week = match self.settings.mode {
            WorkingMode::Weekends => "双休".to_string(),
            WorkingMode::SingleDay => "单休".to_string(),
            WorkingMode::AutoSwitch(WeekName::BigWeek) => "大小周(双休)".to_string(),
            WorkingMode::AutoSwitch(WeekName::SmallWeek) => "大小周(单休)".to_string(),
        };


        self.need_day = match self.working_week.as_str() {
            "双休" | "大小周(双休)" => {
                if weekday == chrono::Weekday::Mon {
                    5 - weekday.number_from_monday()
                } else {
                    6 - weekday.number_from_monday()
                }
            }

            "单休" | "大小周(单休)" => {
                if weekday == chrono::Weekday::Mon {
                    6 - weekday.number_from_monday()
                } else {
                    7 - weekday.number_from_monday()
                }
            }
            _ => panic!("工作模式错误"),
        };

        let fes = [
            ("元旦", 2023, 1, 1),
            ("春节", 2023, 1, 22),
            ("清明节", 2023, 4, 5),
            ("劳动节", 2023, 5, 1),
            ("端午节", 2023, 6, 22),
            ("中秋节", 2023, 9, 29),
            ("国庆节", 2023, 10, 1),
        ];

        let mut name = Vec::new();
        let mut result = Vec::new();

        for (_i, day) in fes.iter().enumerate() {
            let x: (&str, i32, u32, u32) = *day;
            let holiday_name = x.0;
            let year = x.1;
            let month = x.2;
            let day = x.3;
            let festival_date = Local.with_ymd_and_hms(year, month, day, 0, 0, 0).unwrap();
            let today = Local::now();
            let day_until = festival_date.signed_duration_since(today).num_days() as i32;
            if day_until > 0 {
                result.push(day_until);
                name.push(holiday_name.to_string());
            }
        }

        let smallest_day = *result.iter().min().unwrap();
        let smallest_day_index = result.iter().position(|&x| x == smallest_day).unwrap();

        self.holiday_name = name[smallest_day_index].clone();
        self.result_day = smallest_day;
    }
}

impl Application for Holiday {
    type Message = Message;
    type Executor = iced::executor::Default;
    type Flags = ();
    type Theme = Theme;

    fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) {
        let settings = SettingsData::load();
        let mode_picklist_state = Some(settings.mode);

        let mut holiday = Self {
            holiday_name: String::new(),
            result_day: 0,
            mode_picklist_state,
            settings,
            need_day: 0,
            working_week: String::new(),
        };
        holiday.update_values();

        let command = Command::perform(async {}, move |_| Message::ModeSelected(settings.mode));
        (holiday, command)
    }

    fn title(&self) -> String {
        String::from("Holiday")
    }

    fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
        match message {
            Message::ModeSelected(mode) => {
                self.update_settings(mode);
                self.settings.save();
                self.mode_picklist_state = Some(self.settings.mode);
                self.update_values();
                Command::none()
            }
        }
    }

    fn view(&self) -> Element<Message> {
        let pick_list = pick_list(
            &WorkingMode::ALL[..],
            self.mode_picklist_state,
            Message::ModeSelected,
        )
        .placeholder("选择模式")
      
        .text_size(16);

        let content = if self.need_day == 0 {
            format!(
                "{}\n周末啦!\n距离{}还有{}天!",
                self.working_week, self.holiday_name, self.result_day
            )
        } else {
            format!(
                "{}\n还需【{}】天就到周末啦!\n距离【{}】还有【{}】天!",
                self.working_week, self.need_day, self.holiday_name, self.result_day
            )
        };

        let content = column![
            vertical_space(0),
            "",
            pick_list,
            vertical_space(20),
            text(content)
        ]
        .align_items(Alignment::Center)
        .spacing(10);

        container(scrollable(content))
            .width(Length::Fill)
            .height(Length::Fill)
            .center_x()
            .into()
    }
}

pub fn main() -> iced::Result {
    SettingsData::load();

    Holiday::run(Settings {
        // 窗口属性
        window: iced::window::Settings {
            size: (270, 200),
            resizable: false,
            decorations: true,
            transparent: false,
            always_on_top: false,
            position: Position::Centered,
            ..iced::window::Settings::default()
        },

        // 字体路径
        default_font: Some(include_bytes!(
            r#"..\HarmonyOS_Sans_SC_Regular.ttf"#
        )),
        ..Settings::default()
    })
}

这个iced有个坑,那就是中文会乱码,所以得设置一个字体,打包的时候会把字体也包进去(猜测。

一个字体8MB,总共应用才14MB。

上效果!

就是这样,非常简陋,但是有靓点(应该算)!

界面.png工作模式.png

就是这个大小周,他是会自动切换的,运行首次设置一下本周是大周还是小周,如果这周设置为[大小周(单休)],那下周就会自动切换为[大小周(双休)]单休、双休,顾名思义,就是一直单休或者一直双休

问题

现在的问题是,没有实现自动获取节假日,只在代码里写死了今年有假放的节日trollface

因为我还不会别的,只会一捏捏基础。打算今年给他完善一下(flag

因为明年就不是那个时间点的节日了😂

9 打赏
打赏 10 积分后可见