Страх и ужас
parent
21ca8b9bca
commit
aecee8b8d0
227
code/src/app.rs
227
code/src/app.rs
|
|
@ -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_dock::{DockArea, Style, TabViewer};
|
||||||
|
use egui_extras::{Column, TableBuilder};
|
||||||
use sqlx::types::BigDecimal;
|
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;
|
static TABS_CAN_BE_WINDOWS: bool = false;
|
||||||
|
|
||||||
|
|
@ -19,6 +20,8 @@ enum TabTypes{
|
||||||
Settings,
|
Settings,
|
||||||
WorkerList,
|
WorkerList,
|
||||||
WorkerPosition,
|
WorkerPosition,
|
||||||
|
MaterialList,
|
||||||
|
MaterialTypeList
|
||||||
}
|
}
|
||||||
struct Tab{
|
struct Tab{
|
||||||
tab_type: TabTypes,
|
tab_type: TabTypes,
|
||||||
|
|
@ -97,6 +100,8 @@ struct MainTabViewer {
|
||||||
db_oper: DBOperator,
|
db_oper: DBOperator,
|
||||||
worker_tabs: WorkerTabViewer,
|
worker_tabs: WorkerTabViewer,
|
||||||
worker_tree: egui_dock::DockState<Tab>,
|
worker_tree: egui_dock::DockState<Tab>,
|
||||||
|
material_tabs: MaterialTabViewer,
|
||||||
|
material_tree: egui_dock::DockState<Tab>,
|
||||||
rt: tokio::runtime::Runtime,
|
rt: tokio::runtime::Runtime,
|
||||||
is_dark_theme: bool,
|
is_dark_theme: bool,
|
||||||
interface_scale_ratio: f32,
|
interface_scale_ratio: f32,
|
||||||
|
|
@ -125,6 +130,17 @@ impl Default for MainTabViewer{
|
||||||
tab_type:TabTypes::WorkerPosition,
|
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,
|
rt,
|
||||||
is_dark_theme: true,
|
is_dark_theme: true,
|
||||||
interface_scale_ratio: 1.2,
|
interface_scale_ratio: 1.2,
|
||||||
|
|
@ -192,7 +208,10 @@ impl MainTabViewer{
|
||||||
.show_inside(ui, &mut self.worker_tabs);
|
.show_inside(ui, &mut self.worker_tabs);
|
||||||
}
|
}
|
||||||
fn show_material(&mut self, ui: &mut egui::Ui){
|
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){
|
fn show_salary(&mut self, ui: &mut egui::Ui){
|
||||||
ui.label("Помогите");
|
ui.label("Помогите");
|
||||||
|
|
@ -622,3 +641,203 @@ impl Default for WorkerTabViewer{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@ use sqlx::mysql::MySqlPool;
|
||||||
|
|
||||||
|
|
||||||
use dotenvy::dotenv_override;
|
use dotenvy::dotenv_override;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
// use crate::schema::equipment::dsl::*;
|
// use crate::schema::equipment::dsl::*;
|
||||||
// use crate::schema::worker::dsl::*;
|
// use crate::schema::worker::dsl::*;
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
|
use anyhow::anyhow;
|
||||||
|
|
||||||
pub struct DBOperator{
|
pub struct DBOperator{
|
||||||
pool: MySqlPool,
|
pool: MySqlPool,
|
||||||
|
|
@ -54,6 +56,33 @@ impl DBOperator{
|
||||||
}
|
}
|
||||||
Ok(rets)
|
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>{
|
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?;
|
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{
|
if ret.len() > 0{
|
||||||
|
|
@ -136,6 +165,8 @@ impl DBOperator{
|
||||||
});
|
});
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
use std::{any::TypeId, ops::Deref};
|
||||||
|
|
||||||
|
use egui::TextBuffer;
|
||||||
use sqlx::types::{BigDecimal, chrono::DateTime};
|
use sqlx::types::{BigDecimal, chrono::DateTime};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,11 +54,24 @@ pub struct EquipmentRow{
|
||||||
pub maintenance_date: chrono::DateTime<chrono::Local>,
|
pub maintenance_date: chrono::DateTime<chrono::Local>,
|
||||||
pub worker_id: i32,
|
pub worker_id: i32,
|
||||||
}
|
}
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Material{
|
pub struct Material{
|
||||||
id: i32,
|
pub id: i32,
|
||||||
name: String,
|
pub name: String,
|
||||||
quantity: i32,
|
pub quantity: i32,
|
||||||
category: String
|
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{
|
pub struct RecipeElement{
|
||||||
id:i32,
|
id:i32,
|
||||||
|
|
@ -93,3 +109,50 @@ pub struct Product<'a>{
|
||||||
price_per_unit: rust_decimal::Decimal,
|
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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue