Compare commits

...

2 Commits

4 changed files with 322 additions and 9 deletions

View File

@ -2,7 +2,7 @@
Система позволяет вести учёт:
- [ ] Оборудования
- [ ] Сотрудников
- [x] Сотрудников
- [ ] Сырья
- [ ] Произведённой продукции
- [ ] Доходов от продажи продукции

View File

@ -1,9 +1,10 @@
use std::sync::Arc;
use std::{collections::HashMap, ops::Deref, sync::Arc};
use egui::mutex::RwLock;
use egui::{TextBuffer, mutex::RwLock};
use egui_dock::{DockArea, Style, TabViewer};
use egui_extras::{Column, TableBuilder};
use sqlx::types::BigDecimal;
use crate::{database::DBOperator, models::{Equipment, Position, Worker}};
use crate::{database::DBOperator, models::{Equipment, Material, MaterialCategory, ModalDataType, ModalWinState, Position, Worker}};
static TABS_CAN_BE_WINDOWS: bool = false;
@ -19,6 +20,8 @@ enum TabTypes{
Settings,
WorkerList,
WorkerPosition,
MaterialList,
MaterialTypeList
}
struct Tab{
tab_type: TabTypes,
@ -97,6 +100,8 @@ struct MainTabViewer {
db_oper: DBOperator,
worker_tabs: WorkerTabViewer,
worker_tree: egui_dock::DockState<Tab>,
material_tabs: MaterialTabViewer,
material_tree: egui_dock::DockState<Tab>,
rt: tokio::runtime::Runtime,
is_dark_theme: bool,
interface_scale_ratio: f32,
@ -125,6 +130,17 @@ impl Default for MainTabViewer{
tab_type:TabTypes::WorkerPosition,
}
]),
material_tabs: MaterialTabViewer::default(),
material_tree: egui_dock::DockState::new(vec![
Tab{
title:"Сырьё".to_owned(),
tab_type: TabTypes::MaterialList,
},
Tab{
title:"Тип".to_owned(),
tab_type: TabTypes::MaterialTypeList,
}
]),
rt,
is_dark_theme: true,
interface_scale_ratio: 1.2,
@ -192,7 +208,10 @@ impl MainTabViewer{
.show_inside(ui, &mut self.worker_tabs);
}
fn show_material(&mut self, ui: &mut egui::Ui){
ui.label("Сырьё короче да");
let id = ui.make_persistent_id("MaterialMenu");
DockArea::new(&mut self.material_tree)
.id(id)
.show_inside(ui, &mut self.material_tabs);
}
fn show_salary(&mut self, ui: &mut egui::Ui){
ui.label("Помогите");
@ -621,4 +640,204 @@ impl Default for WorkerTabViewer{
rt,
}
}
}
struct MaterialTabViewer{
db_oper: DBOperator,
rt: tokio::runtime::Runtime,
process_mcat_state: ModalWinState,
process_material_state: ModalWinState,
mat_cats: Arc<RwLock<Vec<MaterialCategory>>>,
mats: Arc<RwLock<Vec<Material>>>,
}
impl MaterialTabViewer {
fn show_material(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui|{
if ui.button("Добавить").clicked(){
self.process_material_state.is_open = true;
self.process_material_state.status = "add".to_owned();
}
if ui.button("Редактировать").clicked(){
self.process_material_state.is_open = true;
self.process_material_state.status = "edit".to_owned();
}
if ui.button("Удалить").clicked(){
self.process_material_state.is_open = true;
self.process_material_state.status = "remove".to_owned();
}
if ui.button("Обновить").clicked(){
}
});
if self.process_material_state.is_open{
egui::Modal::new("add_material".into()).show(ui.ctx(), |ui|{
if ui.button("Закрыть").clicked(){
self.process_material_state.is_open = false;
}
});
}
TableBuilder::new(ui)
.striped(true)
.cell_layout(egui::Layout::centered_and_justified(egui::Direction::LeftToRight))
.vscroll(true)
.column(Column::auto())
.column(Column::auto())
.column(Column::auto())
.column(Column::auto())
.header(20.0, |mut header|{
header.col(|ui|{ui.heading("ID");});
header.col(|ui|{ui.heading("Название");});
header.col(|ui|{ui.heading("Количество");});
header.col(|ui|{ui.heading("Тип");});
})
.body(|mut body|{
self.rt.block_on(async{
for mat in self.mats.read().clone().iter(){
body.row(30.0,|mut row|{
row.col(|ui|{ui.label(mat.id.to_string());});
row.col(|ui|{ui.label(&mat.name);});
row.col(|ui|{ui.label(mat.quantity.to_string());});
row.col(|ui|{ui.label(&mat.category.name);});
});
}
})
});
}
fn show_material_type(&mut self, ui: &mut egui::Ui) {
ui.horizontal(|ui|{
if ui.button("Добавить").clicked(){
self.process_mcat_state.is_open = true;
self.process_mcat_state.status = "add".to_owned();
}
if ui.button("Редактировать").clicked(){
self.process_mcat_state.is_open = true;
self.process_mcat_state.status = "edit".to_owned();
}
if ui.button("Удалить").clicked(){
self.process_mcat_state.is_open = true;
self.process_mcat_state.status = "remove".to_owned();
}
if ui.button("Обновить").clicked(){
}
});
if self.process_mcat_state.is_open{
egui::Modal::new("process_mcat".into()).show(ui.ctx(), |ui|{
if ui.button("Закрыть").clicked(){
self.process_mcat_state.is_open = false;
}
egui::Grid::new("process_mcat_grid")
.show(ui, |ui|{
ui.label("Тип");
// println!("{}",self.process_mcat_state.data.get("name").unwrap().as_str());
let mut iterr = self.mat_cats.read().clone();
let selected_id = self.process_mcat_state.data.get("id")
.map(|s| s.as_str()) // если там что-то, у чего есть as_str()
.unwrap_or("");
let current_name = iterr.iter()
.find(|mc| mc.id.to_string() == *selected_id)
.map(|mc | mc.name.as_str())
.unwrap_or("Выбрать");
egui::ComboBox::new("process_mcat_grid_selector","Выбрать")
.selected_text(current_name)
.show_ui(ui, |ui|{
self.rt.block_on(async{
for mcat in iterr{
ui.selectable_value(self.process_mcat_state.data.get_mut("id").unwrap(), mcat.id.to_string(), &mcat.name);
}
});
});
ui.end_row();
ui.label("Название");
ui.text_edit_singleline(self.process_mcat_state.data.get_mut("name").unwrap());
});
});
}
TableBuilder::new(ui)
.striped(true)
.column(Column::auto())
.column(Column::auto())
.header(20.0, |mut header|{
header.col(|ui|{
ui.heading("ID");
});
header.col(|ui|{
ui.heading("Название");
});
})
.body(|mut body|{
self.rt.block_on(async{
for mcat in self.mat_cats.read().clone().iter(){
body.row(20.0,|mut row|{
row.col(|ui|{ui.label(mcat.id.to_string());});
row.col(|ui|{ui.label(&mcat.name);});
});
}
});
});
}
}
impl egui_dock::TabViewer for MaterialTabViewer{
type Tab = Tab;
fn title(&mut self, tab: &mut Self::Tab) -> egui::WidgetText {
(&*tab.title).into()
}
fn ui(&mut self, ui: &mut egui::Ui, tab: &mut Self::Tab) {
match &tab.tab_type{
TabTypes::MaterialList => self.show_material(ui),
TabTypes::MaterialTypeList => self.show_material_type(ui),
_ => {ui.label("Каким образом?");},
}
}
fn allowed_in_windows(&self, _tab: &mut Self::Tab) -> bool {
TABS_CAN_BE_WINDOWS
}
fn is_closeable(&self, _tab: &Self::Tab) -> bool {
false
}
}
impl Default for MaterialTabViewer{
fn default() -> Self {
let rt = tokio::runtime::Runtime::new().unwrap();
let db_oper = rt.block_on(async{DBOperator::new().await});
let mat_cats = Arc::new(RwLock::new(rt.block_on(async{db_oper.get_mcat().await.unwrap()})));
Self {
mats: Arc::new(RwLock::new(rt.block_on(async{db_oper.get_materials().await.unwrap()}))),
db_oper,
rt,
process_mcat_state: ModalWinState{
data: HashMap::from([
(String::from("id"), mat_cats.clone().read().clone()[0].id.to_string()),
(String::from("name"), mat_cats.clone().read().clone()[0].name.clone()),
]),
status: "none".to_owned(),
..Default::default()
},
process_material_state: ModalWinState{
data: HashMap::from([
("id".to_owned(),String::new()),
("name".to_owned(), String::new()),
("category_id".to_owned(), "1".to_owned())
]),
..Default::default()
},
mat_cats,
}
}
}

View File

@ -3,10 +3,12 @@ use sqlx::mysql::MySqlPool;
use dotenvy::dotenv_override;
use std::collections::HashMap;
use std::env;
// use crate::schema::equipment::dsl::*;
// use crate::schema::worker::dsl::*;
use crate::models::*;
use anyhow::anyhow;
pub struct DBOperator{
pool: MySqlPool,
@ -54,6 +56,33 @@ impl DBOperator{
}
Ok(rets)
}
pub async fn get_mcat(&self) -> Result<Vec<MaterialCategory>,sqlx::Error>{
let rets = sqlx::query_as::<_, MaterialCategory>("SELECT * FROM `material_category`").fetch_all(&self.pool).await?;
Ok(rets)
}
pub async fn get_materials(&self) -> Result<Vec<Material>, sqlx::Error>{
let mats = sqlx::query_as::<_,MaterialRow>("SELECT * FROM `material`").fetch_all(&self.pool).await?;
let cats = self.get_mcat().await?;
let cat_map : HashMap<_, MaterialCategory> = cats
.into_iter()
.map(|cat| (cat.id, cat))
.collect();
let mut rets = Vec::with_capacity(mats.len());
for mat in mats{
let cat = cat_map
.get(&mat.category_id)
.expect("Никто не знает как, но БД не смогла связать материал с его категорией. Заставьте разраба это починить.")
.clone();
rets.push(Material{
id: mat.id,
name: mat.name,
quantity: mat.quantity,
category: cat,
});
}
Ok(rets)
}
pub async fn check_worker(&self, worker: Worker) -> Result<bool, sqlx::Error>{
let ret = sqlx::query(&format!("SELECT * FROM `worker` WHERE full_name = {}, position_id = {}, hire_date = {}", worker.full_name, worker.position.id, worker.hire_date.to_string())).fetch_all(&self.pool).await?;
if ret.len() > 0{
@ -136,6 +165,8 @@ impl DBOperator{
});
ret
}
}

View File

@ -1,3 +1,6 @@
use std::{any::TypeId, ops::Deref};
use egui::TextBuffer;
use sqlx::types::{BigDecimal, chrono::DateTime};
@ -51,11 +54,24 @@ pub struct EquipmentRow{
pub maintenance_date: chrono::DateTime<chrono::Local>,
pub worker_id: i32,
}
#[derive(Clone)]
pub struct Material{
id: i32,
name: String,
quantity: i32,
category: String
pub id: i32,
pub name: String,
pub quantity: i32,
pub category: MaterialCategory
}
#[derive(sqlx::FromRow)]
pub struct MaterialRow{
pub id: i32,
pub name: String,
pub quantity: i32,
pub category_id: i32,
}
#[derive(sqlx::FromRow, Default, Clone, PartialEq)]
pub struct MaterialCategory{
pub id: i32,
pub name: String,
}
pub struct RecipeElement{
id:i32,
@ -92,4 +108,51 @@ pub struct Product<'a>{
volume: f64,
price_per_unit: rust_decimal::Decimal,
}
#[derive(Default)]
pub struct ModalWinState{
pub is_open: bool,
pub can_finish: bool,
pub data: std::collections::HashMap<String,String>,
pub status: String,
}
#[derive(PartialEq)]
pub enum ModalDataType{
Pos(Position),
MatCat(MaterialCategory),
Text(String),
}
impl TextBuffer for ModalDataType{
fn is_mutable(&self) -> bool {
true
}
fn as_str(&self) -> &str {
match self{
ModalDataType::Pos(pos) => &pos.name,
ModalDataType::Text(txt) => &txt,
ModalDataType::MatCat(mat_cat) => &mat_cat.name,
}
}
fn insert_text(&mut self, text: &str, char_index: usize) -> usize {
match self{
ModalDataType::Pos(pos) => {pos.name.insert_str(char_index, text);},
ModalDataType::Text(txt) => {txt.insert_str(char_index, text);},
ModalDataType::MatCat(mat_cat) => {mat_cat.name.insert_str(char_index, text);}
}
text.len()
}
fn delete_char_range(&mut self, char_range: std::ops::Range<usize>) {
match self{
ModalDataType::Pos(pos) => {pos.name.delete_char_range(char_range);},
ModalDataType::Text(txt) => {txt.delete_char_range(char_range);},
ModalDataType::MatCat(mat_cat) => {mat_cat.name.delete_char_range(char_range);}
}
}
fn type_id(&self) -> std::any::TypeId {
TypeId::of::<ModalDataType>()
}
}