陈雪 пре 2 дана
родитељ
комит
1ef098a96b

+ 25 - 4
README.md

@@ -1,7 +1,28 @@
-# Tauri + Vue 3
+# 盼达点餐系统
 
-This template should help get you started developing with Tauri + Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+## 数据库操作
 
-## Recommended IDE Setup
+1. 菜品信息
+   1. 菜品分类
+   2. 菜品粗略信息
+   3. sku 信息
+   4. 规格信息
+2. 桌子管理
+   1. 区域管理
+   2. 桌号管理
+3. 订单管理
+   1. 创建订单
+   2. 订单同步
+4. 材料管理
 
-- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
+## 数据同步
+
+1. 二维码展示&扫码
+2. 服务端 API
+   1. 获取分类
+   2. 获取商品
+   3. 订单创建
+   4. 桌子状态
+      1. 添加挂单
+      2. 追加菜品
+      3. 取消挂单/结账

+ 155 - 37
src-tauri/resources/table_struct.sql

@@ -2,43 +2,32 @@
 CREATE TABLE
     menu_cate (
         id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 主键
-        name TEXT DEFAULT '', -- 分类名称
-        logo TEXT DEFAULT '', -- 分类Logo
-        local_logo TEXT DEFAULT '', -- 本地Logo路径
+        name TEXT NOT NULL DEFAULT '', -- 分类名称
+        logo TEXT NOT NULL DEFAULT '', -- 分类Logo
         description TEXT, -- 分类描述
-        create_time DATETIME DEFAULT NULL, -- 创建时间
-        update_time DATETIME DEFAULT NULL, -- 更新时间
-        sort INTEGER DEFAULT 0, -- 排序
-        status TEXT DEFAULT 'A' -- 状态 (A-Active, D-Deleted)
+        create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
+        update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
+        sort INTEGER NOT NULL DEFAULT 0, -- 排序
+        status INTEGER NOT NULL DEFAULT 1 -- 状态 (1-Active, 0-Inactive)
     );
 
 -- 创建商品表
 CREATE TABLE
     menu_commodity (
         id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 主键
-        type TEXT DEFAULT 'product', -- 商品类型
-        name TEXT DEFAULT '', -- 商品名称
-        cate_id INTEGER DEFAULT 0, -- 分类ID
-        goods_no TEXT DEFAULT '', -- 商品编号
-        is_single_spec TEXT DEFAULT 'Y', -- 是否单规格 (Y-Yes, N-No)
-        logo TEXT DEFAULT '', -- 商品Logo
-        images TEXT DEFAULT '', -- 商品图片
-        price REAL DEFAULT 0.00, -- 价格
-        line_price REAL DEFAULT 0.00, -- 划线价
-        stock INTEGER DEFAULT 0, -- 库存
-        weight REAL DEFAULT 0.00, -- 重量
-        coupon_ids TEXT DEFAULT '', -- 优惠券ID
-        service_time INTEGER DEFAULT 0, -- 服务时间
-        init_sale INTEGER DEFAULT 0, -- 初始销量
-        sale_point TEXT DEFAULT '', -- 卖点
-        can_use_point TEXT DEFAULT 'N', -- 是否可用积分 (Y-Yes, N-No)
-        is_member_discount TEXT DEFAULT 'Y', -- 是否会员折扣 (Y-Yes, N-No)
-        sort INTEGER DEFAULT 0, -- 排序
-        description TEXT, -- 商品描述
-        create_time DATETIME DEFAULT NULL, -- 创建时间
-        update_time DATETIME DEFAULT NULL, -- 更新时间
-        operator TEXT DEFAULT NULL, -- 操作员
-        status TEXT DEFAULT 'A', -- 状态 (A-Active, D-Deleted)
+        name TEXT NOT NULL DEFAULT '', -- 商品名称
+        cate_id INTEGER NOT NULL DEFAULT 0, -- 分类ID
+        goods_no TEXT NOT NULL DEFAULT '', -- 商品编号
+        is_single_spec INTEGER NOT NULL DEFAULT 1, -- 是否单规格 (1-单规格, 0-多规格)
+        logo TEXT NOT NULL DEFAULT '', -- 商品Logo
+        price REAL NOT NULL DEFAULT 0.00, -- 价格
+        line_price REAL NOT NULL DEFAULT 0.00, -- 商品原价
+        stock INTEGER NOT NULL DEFAULT 0, -- 库存
+        sort INTEGER NOT NULL DEFAULT 0, -- 排序
+        description TEXT NOT NULL DEFAULT '', -- 商品描述
+        create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
+        update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
+        status INTEGER NOT NULL DEFAULT 1, -- 状态 (1-Active, 0-Inactive),
         FOREIGN KEY (cate_id) REFERENCES menu_cate (id)
     );
 
@@ -46,14 +35,13 @@ CREATE TABLE
 CREATE TABLE
     menu_sku (
         id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 主键
-        sku_no TEXT DEFAULT '', -- SKU编号
-        logo TEXT DEFAULT '', -- SKU Logo
+        sku_no TEXT NOT NULL DEFAULT '', -- SKU编号
+        logo TEXT NOT NULL DEFAULT '', -- SKU Logo
         goods_id INTEGER NOT NULL DEFAULT 0, -- 商品ID
         stock INTEGER NOT NULL DEFAULT 0, -- 库存
         price REAL NOT NULL DEFAULT 0.00, -- 价格
-        line_price REAL NOT NULL DEFAULT 0.00, -- 划线价
-        weight REAL DEFAULT 0.00, -- 重量
-        status TEXT NOT NULL DEFAULT 'A', -- 状态 (A-Active, D-Deleted)
+        line_price REAL NOT NULL DEFAULT 0.00, -- 商品原价
+        status INTEGER NOT NULL DEFAULT 1, -- 状态 (1-Active, 0-Inactive),
         FOREIGN KEY (goods_id) REFERENCES menu_commodity (id)
     );
 
@@ -64,7 +52,7 @@ CREATE TABLE
         goods_id INTEGER NOT NULL DEFAULT 0, -- 商品ID
         name TEXT NOT NULL DEFAULT '', -- 规格名称
         value TEXT NOT NULL DEFAULT '', -- 规格值
-        status TEXT DEFAULT 'A', -- 状态 (A-Active, D-Deleted)
+        status INTEGER NOT NULL DEFAULT 1, -- 状态 (1-Active, 0-Inactive),
         FOREIGN KEY (goods_id) REFERENCES menu_commodity (id)
     );
 
@@ -76,4 +64,134 @@ CREATE TABLE
         PRIMARY KEY (sku_id, spec_id),
         FOREIGN KEY (sku_id) REFERENCES menu_sku (id),
         FOREIGN KEY (spec_id) REFERENCES menu_spec (id)
-    );
+    );
+
+-- 创建店铺订单表信息
+CREATE TABLE
+    store_order (
+        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 自增ID
+        order_sn TEXT NOT NULL DEFAULT '', -- 订单号
+        table_id INTEGER NOT NULL, -- 所属桌码ID
+        pay_type INTEGER NOT NULL DEFAULT 0, -- 支付方式 (1-现金, 0-Paypal)
+        amount REAL NOT NULL DEFAULT 0.00, -- 订单金额
+        pay_amount REAL NOT NULL DEFAULT 0.00, -- 支付金额
+        discount REAL NOT NULL DEFAULT 0.00, -- 折扣金额
+        param TEXT NOT NULL DEFAULT '', -- 订单参数
+        service_fee REAL DEFAULT NULL, -- 服务费
+        remark TEXT NOT NULL DEFAULT '', -- 用户备注
+        create_time DATETIME DEFAULT NULL, -- 创建时间
+        update_time DATETIME DEFAULT NULL, -- 更新时间
+        status TEXT NOT NULL DEFAULT 'A', -- 订单状态
+        pay_time DATETIME DEFAULT NULL, -- 支付时间
+        pay_status INTEGER NOT NULL DEFAULT 1, -- 支付状态
+        settle_status INTEGER NOT NULL DEFAULT 1, -- 结算状态
+        goods TEXT NOT NULL DEFAULT '{}' -- 订单商品信息
+    );
+
+-- 创建店铺材料表信息
+CREATE TABLE
+    store_material (
+        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 材料表id
+        material_no TEXT NOT NULL, -- 材料编号
+        material_type TEXT NOT NULL, -- 材料类别
+        name TEXT NOT NULL, -- 材料名称
+        specification TEXT NOT NULL, -- 规格
+        inventory INTEGER NOT NULL DEFAULT 0, -- 库存数量
+        shelf_life INTEGER NOT NULL DEFAULT 0, -- 保质期
+        unit TEXT NOT NULL, -- 单位
+        img TEXT NOT NULL, -- 材料文件/图片id
+        purchase_period INTEGER NOT NULL DEFAULT 0, -- 采购期数
+        create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
+        update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
+        status INTEGER NOT NULL DEFAULT 1 -- 状态0下架1上架
+    );
+
+-- 店铺区域表
+CREATE TABLE
+    store_area (
+        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 区域id
+        name TEXT NOT NULL, -- 区域名称
+        description TEXT NOT NULL DEFAULT '', -- 区域描述
+        create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
+        update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
+        status INTEGER NOT NULL DEFAULT 1 -- 状态 (1-有效, 0-无效)
+    );
+
+-- 店铺座位表
+CREATE TABLE
+    store_table (
+        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, -- 桌子id
+        area_id INTEGER NOT NULL, -- 所属区域id
+        table_no TEXT NOT NULL, -- 桌号
+        capacity INTEGER NOT NULL DEFAULT 0, -- 座位数
+        create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 创建时间
+        update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- 更新时间
+        status INTEGER NOT NULL DEFAULT 1, -- 状态 (1-有效, 0-无效)
+        FOREIGN KEY (area_id) REFERENCES store_area (id)
+    );
+
+-- 新增菜单分类表的更新时间触发器
+CREATE TRIGGER trg_menu_cate_update_time AFTER
+UPDATE ON menu_cate FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE menu_cate
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;
+
+-- 新增商品表的更新时间触发器
+CREATE TRIGGER trg_menu_commodity_update_time AFTER
+UPDATE ON menu_commodity FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE menu_commodity
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;
+
+-- 新增订单表的更新时间触发器
+CREATE TRIGGER trg_order_update_time AFTER
+UPDATE ON store_order FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE store_order
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;
+
+-- 新增材料表的更新时间触发器
+CREATE TRIGGER trg_material_update_time AFTER
+UPDATE ON store_material FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE store_material
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;
+
+-- 新增区域表的更新时间触发器
+CREATE TRIGGER trg_area_update_time AFTER
+UPDATE ON store_area FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE store_area
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;
+
+-- 新增桌子表的更新时间触发器
+CREATE TRIGGER trg_table_update_time AFTER
+UPDATE ON store_table FOR EACH ROW WHEN NEW.update_time = OLD.update_time BEGIN
+UPDATE store_table
+SET
+    update_time = CURRENT_TIMESTAMP
+WHERE
+    id = OLD.id;
+
+END;

+ 2 - 2
src-tauri/src/commands/mod.rs

@@ -1,12 +1,12 @@
 use tauri::State;
 
 use crate::{
-    AppState, CommandResult,
+    AppState, CmdResult,
     service::category::{Category, CategoryWithSpu},
 };
 
 #[tauri::command]
-pub async fn get_category(app_state: State<'_, AppState>) -> CommandResult<Vec<CategoryWithSpu>> {
+pub async fn get_category(app_state: State<'_, AppState>) -> CmdResult<Vec<CategoryWithSpu>> {
     let category = Category::new(app_state.conn.clone());
     let categories = category.get_category_commodity().await;
     match categories {

+ 1 - 1
src-tauri/src/lib.rs

@@ -7,7 +7,7 @@ use std::fs::{self, File};
 use std::sync::Arc;
 use tauri::{AppHandle, Manager, path::BaseDirectory};
 
-type CommandResult<T> = Result<T, String>;
+type CmdResult<T> = Result<T, String>;
 // 添加 AppState 结构体
 #[derive(Default)]
 pub struct AppState {

+ 3 - 6
src-tauri/src/models/menu_cate.rs

@@ -1,9 +1,9 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
 
 use sea_orm::entity::prelude::*;
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[sea_orm(table_name = "menu_cate")]
 pub struct Model {
     #[sea_orm(primary_key)]
@@ -13,16 +13,13 @@ pub struct Model {
     #[sea_orm(column_type = "Text", nullable)]
     pub logo: Option<String>,
     #[sea_orm(column_type = "Text", nullable)]
-    pub local_logo: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
     pub description: Option<String>,
     #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
     pub create_time: Option<String>,
     #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
     pub update_time: Option<String>,
     pub sort: Option<i32>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub status: Option<String>,
+    pub status: Option<i32>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

+ 4 - 23
src-tauri/src/models/menu_commodity.rs

@@ -1,40 +1,24 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
 
 use sea_orm::entity::prelude::*;
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[sea_orm(table_name = "menu_commodity")]
 pub struct Model {
     #[sea_orm(primary_key)]
     pub id: i32,
     #[sea_orm(column_type = "Text", nullable)]
-    pub r#type: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
     pub name: Option<String>,
     pub cate_id: Option<i32>,
     #[sea_orm(column_type = "Text", nullable)]
     pub goods_no: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub is_single_spec: Option<String>,
+    pub is_single_spec: Option<i32>,
     #[sea_orm(column_type = "Text", nullable)]
     pub logo: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub images: Option<String>,
     pub price: Option<Decimal>,
     pub line_price: Option<Decimal>,
     pub stock: Option<i32>,
-    pub weight: Option<Decimal>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub coupon_ids: Option<String>,
-    pub service_time: Option<i32>,
-    pub init_sale: Option<i32>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub sale_point: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub can_use_point: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub is_member_discount: Option<String>,
     pub sort: Option<i32>,
     #[sea_orm(column_type = "Text", nullable)]
     pub description: Option<String>,
@@ -42,10 +26,7 @@ pub struct Model {
     pub create_time: Option<String>,
     #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
     pub update_time: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub operator: Option<String>,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub status: Option<String>,
+    pub status: Option<i32>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

+ 3 - 4
src-tauri/src/models/menu_sku.rs

@@ -1,8 +1,9 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
 
 use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Deserialize, Serialize)]
 #[sea_orm(table_name = "menu_sku")]
 pub struct Model {
     #[sea_orm(primary_key)]
@@ -15,9 +16,7 @@ pub struct Model {
     pub stock: i32,
     pub price: Decimal,
     pub line_price: Decimal,
-    pub weight: Option<Decimal>,
-    #[sea_orm(column_type = "Text")]
-    pub status: String,
+    pub status: Option<i32>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

+ 3 - 3
src-tauri/src/models/menu_sku_spec.rs

@@ -1,9 +1,9 @@
 //! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
 
 use sea_orm::entity::prelude::*;
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
 
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
 #[sea_orm(table_name = "menu_sku_spec")]
 pub struct Model {
     #[sea_orm(primary_key, auto_increment = false)]
@@ -12,7 +12,7 @@ pub struct Model {
     pub spec_id: i32,
 }
 
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation, Serialize)]
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
 pub enum Relation {
     #[sea_orm(
         belongs_to = "super::menu_sku::Entity",

+ 1 - 2
src-tauri/src/models/menu_spec.rs

@@ -13,8 +13,7 @@ pub struct Model {
     pub name: String,
     #[sea_orm(column_type = "Text")]
     pub value: String,
-    #[sea_orm(column_type = "Text", nullable)]
-    pub status: Option<String>,
+    pub status: Option<i32>,
 }
 
 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

+ 4 - 0
src-tauri/src/models/mod.rs

@@ -7,3 +7,7 @@ pub mod menu_commodity;
 pub mod menu_sku;
 pub mod menu_sku_spec;
 pub mod menu_spec;
+pub mod store_area;
+pub mod store_material;
+pub mod store_order;
+pub mod store_table;

+ 7 - 3
src-tauri/src/models/prelude.rs

@@ -2,6 +2,10 @@
 
 pub use super::menu_cate::Entity as MenuCate;
 pub use super::menu_commodity::Entity as MenuCommodity;
-// pub use super::menu_sku::Entity as MenuSku;
-// pub use super::menu_sku_spec::Entity as MenuSkuSpec;
-// pub use super::menu_spec::Entity as MenuSpec;
+pub use super::menu_sku::Entity as MenuSku;
+pub use super::menu_sku_spec::Entity as MenuSkuSpec;
+pub use super::menu_spec::Entity as MenuSpec;
+pub use super::store_area::Entity as StoreArea;
+pub use super::store_material::Entity as StoreMaterial;
+pub use super::store_order::Entity as StoreOrder;
+pub use super::store_table::Entity as StoreTable;

+ 34 - 0
src-tauri/src/models/store_area.rs

@@ -0,0 +1,34 @@
+//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
+
+use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
+#[sea_orm(table_name = "store_area")]
+pub struct Model {
+    #[sea_orm(primary_key)]
+    pub id: i32,
+    #[sea_orm(column_type = "Text")]
+    pub name: String,
+    #[sea_orm(column_type = "Text", nullable)]
+    pub description: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub create_time: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub update_time: Option<String>,
+    pub status: Option<i32>,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {
+    #[sea_orm(has_many = "super::store_table::Entity")]
+    StoreTable,
+}
+
+impl Related<super::store_table::Entity> for Entity {
+    fn to() -> RelationDef {
+        Relation::StoreTable.def()
+    }
+}
+
+impl ActiveModelBehavior for ActiveModel {}

+ 36 - 0
src-tauri/src/models/store_material.rs

@@ -0,0 +1,36 @@
+//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
+
+use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
+#[sea_orm(table_name = "store_material")]
+pub struct Model {
+    #[sea_orm(primary_key)]
+    pub id: i32,
+    #[sea_orm(column_type = "Text")]
+    pub material_no: String,
+    #[sea_orm(column_type = "Text")]
+    pub material_type: String,
+    #[sea_orm(column_type = "Text")]
+    pub name: String,
+    #[sea_orm(column_type = "Text")]
+    pub specification: String,
+    pub inventory: i32,
+    pub shelf_life: i32,
+    #[sea_orm(column_type = "Text")]
+    pub unit: String,
+    #[sea_orm(column_type = "Text")]
+    pub img: String,
+    pub purchase_period: Option<i32>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub create_time: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub update_time: Option<String>,
+    pub status: Option<i32>,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {}
+
+impl ActiveModelBehavior for ActiveModel {}

+ 40 - 0
src-tauri/src/models/store_order.rs

@@ -0,0 +1,40 @@
+//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
+
+use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
+#[sea_orm(table_name = "store_order")]
+pub struct Model {
+    #[sea_orm(primary_key)]
+    pub id: i32,
+    #[sea_orm(column_type = "Text")]
+    pub order_sn: String,
+    pub table_id: i32,
+    pub pay_type: i32,
+    pub amount: Option<Decimal>,
+    pub pay_amount: Option<Decimal>,
+    pub discount: Option<Decimal>,
+    #[sea_orm(column_type = "Text", nullable)]
+    pub param: Option<String>,
+    pub service_fee: Option<Decimal>,
+    #[sea_orm(column_type = "Text", nullable)]
+    pub remark: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub create_time: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub update_time: Option<String>,
+    #[sea_orm(column_type = "Text", nullable)]
+    pub status: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub pay_time: Option<String>,
+    pub pay_status: Option<i32>,
+    pub settle_status: Option<i32>,
+    #[sea_orm(column_type = "Text", nullable)]
+    pub goods: Option<String>,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {}
+
+impl ActiveModelBehavior for ActiveModel {}

+ 40 - 0
src-tauri/src/models/store_table.rs

@@ -0,0 +1,40 @@
+//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.0
+
+use sea_orm::entity::prelude::*;
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
+#[sea_orm(table_name = "store_table")]
+pub struct Model {
+    #[sea_orm(primary_key)]
+    pub id: i32,
+    pub area_id: i32,
+    #[sea_orm(column_type = "Text")]
+    pub table_no: String,
+    pub capacity: Option<i32>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub create_time: Option<String>,
+    #[sea_orm(column_type = "custom(\"DATETIME\")", nullable)]
+    pub update_time: Option<String>,
+    pub status: Option<i32>,
+}
+
+#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
+pub enum Relation {
+    #[sea_orm(
+        belongs_to = "super::store_area::Entity",
+        from = "Column::AreaId",
+        to = "super::store_area::Column::Id",
+        on_update = "NoAction",
+        on_delete = "NoAction"
+    )]
+    StoreArea,
+}
+
+impl Related<super::store_area::Entity> for Entity {
+    fn to() -> RelationDef {
+        Relation::StoreArea.def()
+    }
+}
+
+impl ActiveModelBehavior for ActiveModel {}

+ 7 - 2
src/service/CategoryService.ts

@@ -1,5 +1,10 @@
 import { MenuCategory } from '@/interface/MenuCate'
+import { invoke } from '@tauri-apps/api/core'
 
-export async function getMenuCategories() {
-  return []
+export class CategoryService {
+  static async getAll() {
+    const categories = await invoke<MenuCategory>('get_category')
+    console.log('categories: ', categories)
+    return categories
+  }
 }

+ 184 - 161
src/views/menu/index.vue

@@ -4,24 +4,37 @@
       <div class="w-full h-full overflow-y-auto relative hide-bar">
         <div class="sticky top-0 w-full bg-[#fff] p-[10px]">
           <div class="h-[60px] flex justify-between items-center">
-            <div class="flex-1 h-full pr-[10px] flex items-center justify-between cursor-pointer overflow-hidden">
+            <div
+              class="flex-1 h-full pr-[10px] flex items-center justify-between cursor-pointer overflow-hidden"
+            >
               <div
-              @click="handleArrow(-1)"
-                class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]">
+                @click="handleArrow(-1)"
+                class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]"
+              >
                 <el-icon>
                   <ArrowLeft />
                 </el-icon>
               </div>
-              <div ref="scrollRef" class="overflow-x-auto bg-[#fff]" style="width: calc(100% - 108px)">
+              <div
+                ref="scrollRef"
+                class="overflow-x-auto bg-[#fff]"
+                style="width: calc(100% - 108px)"
+              >
                 <div ref="" class="flex flex-nowrap items-center">
-                  <div @click="selectMenu(index)" v-for="(item,index) in products" class="flex items-center p-2 shrink-0 mr-[20px] h-[34px]" :class="[currentIndex == index ? 'active' : '' ]">
+                  <div
+                    @click="selectMenu(index)"
+                    v-for="(item, index) in products"
+                    class="flex items-center p-2 shrink-0 mr-[20px] h-[34px]"
+                    :class="[currentIndex == index ? 'active' : '']"
+                  >
                     {{ item.name }}
                   </div>
                 </div>
               </div>
               <div
-              @click="handleArrow(1)"
-                class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]">
+                @click="handleArrow(1)"
+                class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]"
+              >
                 <el-icon>
                   <ArrowRight />
                 </el-icon>
@@ -29,14 +42,25 @@
             </div>
           </div>
           <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
-            <div v-for="(product,index) in products" :key="product.id" @click="show = true"
-              class="bg-white rounded-xl shadow-sm transition-transform" >
-              <div class="relative flex items-center bg-[#fef8f4] justify-center mb-4 rounded overflow-hidden">
-                <img :src="product.image" :alt="product.name" class="w-25 h-25 my-1 object-cover rounded-full" />
+            <div
+              v-for="(product, index) in products"
+              :key="product.id"
+              @click="show = true"
+              class="bg-white rounded-xl shadow-sm transition-transform"
+            >
+              <div
+                class="relative flex items-center bg-[#fef8f4] justify-center mb-4 rounded overflow-hidden"
+              >
+                <img
+                  :src="product.image"
+                  :alt="product.name"
+                  class="w-25 h-25 my-1 object-cover rounded-full"
+                />
               </div>
               <div class="text-left px-2">
                 <h3
-                  class="text-gray-800 font-medium text-sm mb-2 text-container w-full text-nowrap line-clamp-2 sm:line-clamp-1 md:line-clamp-2 lg:line-clamp-3">
+                  class="text-gray-800 font-medium text-sm mb-2 text-container w-full text-nowrap line-clamp-2 sm:line-clamp-1 md:line-clamp-2 lg:line-clamp-3"
+                >
                   {{ product.name }}
                 </h3>
                 <p class="text-orange-500 font-semibold">${{ product.price.toFixed(2) }}</p>
@@ -52,162 +76,161 @@
     <OrderDrawer v-model:show="show" />
     <deskDrawer v-model:show="deskShow" />
     <payDrawer v-model:show="payShow" />
-
   </div>
 </template>
-<script  setup>
-import { ref, onMounted, nextTick } from 'vue'
-import { useI18n } from 'vue-i18n'
-import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
-import rightOrder from './components/rightOrder/rightOrder.vue'
-import OrderDrawer from './components/order-drawer.vue'
-import deskDrawer from './components/desk-drawer.vue'
-import payDrawer from './components/pay-drawer.vue'
-const { t } = useI18n()
-
-const show = ref(false)
-
-onMounted(() => { })
-
-const products = ref([
-  {
-    id: 1,
-    name: 'Spicy Seasoned Seafood Noodles',
-    price: 2.29,
-    image: 'https://ai-public.mastergo.com/ai/img_res/e53b11a05b7778db2bc50338e47047c4.jpg',
-  },
-  {
-    id: 2,
-    name: 'Grilled Salmon Bowl',
-    price: 3.49,
-    image: 'https://ai-public.mastergo.com/ai/img_res/07e4619fc2f26fe9f6a0afaebc47dceb.jpg',
-  },
-  {
-    id: 3,
-    name: 'Teriyaki Chicken Rice',
-    price: 2.99,
-    image: 'https://ai-public.mastergo.com/ai/img_res/1709d2e0e3495f8089da24be4ad2d26a.jpg',
-  },
-  {
-    id: 4,
-    name: 'Vegetable Stir Fry Noodles',
-    price: 2.49,
-    image: 'https://ai-public.mastergo.com/ai/img_res/043e50f136a6ec7f5f37eb448cf298c7.jpg',
-  },
-  {
-    id: 5,
-    name: 'Spicy Tuna Poke Bowl',
-    price: 3.99,
-    image: 'https://ai-public.mastergo.com/ai/img_res/7f8ad399ad651c8de6896bc611be1936.jpg',
-  },
-  {
-    id: 6,
-    name: 'Beef Bulgogi Bowl',
-    price: 3.79,
-    image: 'https://ai-public.mastergo.com/ai/img_res/49b0d0fbf6654dcef422a7e3334c8b6a.jpg',
-  },
-  {
-    id: 7,
-    name: 'Shrimp Pad Thai',
-    price: 3.29,
-    image: 'https://ai-public.mastergo.com/ai/img_res/ca1fef96dffcfa401768314721606c8f.jpg',
-  },
-  {
-    id: 8,
-    name: 'Miso Ramen Bowl',
-    price: 2.89,
-    image: 'https://ai-public.mastergo.com/ai/img_res/210debe79bb22fec1944cab575f2759f.jpg',
-  },
-])
-
-// 挂单桌子
-const deskShow = ref(false)
-const catgroyClick = () => {
-  deskShow.value = true
-}
-
-const scrollRef = ref(null);
-const currentIndex = ref(0);
-let positions = [];
-
-const recalculatePositions = () => {
-  const flexContainer = scrollRef.value?.firstElementChild;
-  if (!flexContainer) return [];
-  
-  const items = Array.from(flexContainer.children);
-  let total = 0;
-  
-  return items.map((item, index) => {
-    const style = window.getComputedStyle(item);
-    const margin = index < items.length - 1 ? parseFloat(style.marginRight) : 0;
-    const start = total;
-    total += item.offsetWidth + margin;
-    return { start, end: total - margin };
-  });
-};
-
-const scrollToIndex = (index) => {
-  if (!scrollRef.value) return;
-  
-  const containerWidth = scrollRef.value.offsetWidth;
-  const target = positions[index];
-  
-  if (!target) return;
-  
-  const scrollPos = Math.max(0, Math.min(
-    target.start - (containerWidth - (target.end - target.start)) / 2,
-    scrollRef.value.scrollWidth - containerWidth
-  ));
-  
-  scrollRef.value.scrollTo({
-    left: scrollPos,
-    behavior: 'smooth'
-  });
-};
-
-const handleArrow = (direction) => {
-  positions = recalculatePositions();
-  currentIndex.value = Math.max(0, Math.min(
-    currentIndex.value + direction,
-    positions.length - 1
-  ));
-  scrollToIndex(currentIndex.value);
-};
-
-const selectMenu = (index) => {
-  currentIndex.value = index;
-  scrollToIndex(index);
-};
-
-//pay
-const payShow = ref(false)
-const onPay = ()=>{
-  payShow.value = true
-}
+<script setup>
+  import { ref, onMounted, nextTick } from 'vue'
+  import { useI18n } from 'vue-i18n'
+  import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
+  import rightOrder from './components/rightOrder/rightOrder.vue'
+  import OrderDrawer from './components/order-drawer.vue'
+  import deskDrawer from './components/desk-drawer.vue'
+  import payDrawer from './components/pay-drawer.vue'
+  const { t } = useI18n()
+
+  const show = ref(false)
+
+  onMounted(() => {})
+
+  const products = ref([
+    {
+      id: 1,
+      name: 'Spicy Seasoned Seafood Noodles',
+      price: 2.29,
+      image: 'https://ai-public.mastergo.com/ai/img_res/e53b11a05b7778db2bc50338e47047c4.jpg',
+    },
+    {
+      id: 2,
+      name: 'Grilled Salmon Bowl',
+      price: 3.49,
+      image: 'https://ai-public.mastergo.com/ai/img_res/07e4619fc2f26fe9f6a0afaebc47dceb.jpg',
+    },
+    {
+      id: 3,
+      name: 'Teriyaki Chicken Rice',
+      price: 2.99,
+      image: 'https://ai-public.mastergo.com/ai/img_res/1709d2e0e3495f8089da24be4ad2d26a.jpg',
+    },
+    {
+      id: 4,
+      name: 'Vegetable Stir Fry Noodles',
+      price: 2.49,
+      image: 'https://ai-public.mastergo.com/ai/img_res/043e50f136a6ec7f5f37eb448cf298c7.jpg',
+    },
+    {
+      id: 5,
+      name: 'Spicy Tuna Poke Bowl',
+      price: 3.99,
+      image: 'https://ai-public.mastergo.com/ai/img_res/7f8ad399ad651c8de6896bc611be1936.jpg',
+    },
+    {
+      id: 6,
+      name: 'Beef Bulgogi Bowl',
+      price: 3.79,
+      image: 'https://ai-public.mastergo.com/ai/img_res/49b0d0fbf6654dcef422a7e3334c8b6a.jpg',
+    },
+    {
+      id: 7,
+      name: 'Shrimp Pad Thai',
+      price: 3.29,
+      image: 'https://ai-public.mastergo.com/ai/img_res/ca1fef96dffcfa401768314721606c8f.jpg',
+    },
+    {
+      id: 8,
+      name: 'Miso Ramen Bowl',
+      price: 2.89,
+      image: 'https://ai-public.mastergo.com/ai/img_res/210debe79bb22fec1944cab575f2759f.jpg',
+    },
+  ])
+
+  // 挂单桌子
+  const deskShow = ref(false)
+  const catgroyClick = () => {
+    deskShow.value = true
+  }
+
+  const scrollRef = ref(null)
+  const currentIndex = ref(0)
+  let positions = []
+
+  const recalculatePositions = () => {
+    const flexContainer = scrollRef.value?.firstElementChild
+    if (!flexContainer) return []
+
+    const items = Array.from(flexContainer.children)
+    let total = 0
+
+    return items.map((item, index) => {
+      const style = window.getComputedStyle(item)
+      const margin = index < items.length - 1 ? parseFloat(style.marginRight) : 0
+      const start = total
+      total += item.offsetWidth + margin
+      return { start, end: total - margin }
+    })
+  }
+
+  const scrollToIndex = (index) => {
+    if (!scrollRef.value) return
+
+    const containerWidth = scrollRef.value.offsetWidth
+    const target = positions[index]
+
+    if (!target) return
+
+    const scrollPos = Math.max(
+      0,
+      Math.min(
+        target.start - (containerWidth - (target.end - target.start)) / 2,
+        scrollRef.value.scrollWidth - containerWidth,
+      ),
+    )
+
+    scrollRef.value.scrollTo({
+      left: scrollPos,
+      behavior: 'smooth',
+    })
+  }
+
+  const handleArrow = (direction) => {
+    positions = recalculatePositions()
+    currentIndex.value = Math.max(0, Math.min(currentIndex.value + direction, positions.length - 1))
+    scrollToIndex(currentIndex.value)
+  }
+
+  const selectMenu = (index) => {
+    currentIndex.value = index
+    scrollToIndex(index)
+  }
+
+  //pay
+  const payShow = ref(false)
+  const onPay = () => {
+    payShow.value = true
+  }
 </script>
 <style scoped>
-.active {
-  background-color: #f67f20;
-  color:white;
-  border-radius: 5px;
-}
-::-webkit-scrollbar-thumb {
-  background-color: red;
-}
-
-/* 隐藏整个滚动条 */
-::-webkit-scrollbar {
-  display: none;
-}
+  .active {
+    background-color: #f67f20;
+    color: white;
+    border-radius: 5px;
+  }
+  ::-webkit-scrollbar-thumb {
+    background-color: red;
+  }
+
+  /* 隐藏整个滚动条 */
+  ::-webkit-scrollbar {
+    display: none;
+  }
 
-/* 或者仅隐藏滚动条的轨道,保留滑块可操作性,但视觉上不显示滚动条 */
-/* ::-webkit-scrollbar-track {
+  /* 或者仅隐藏滚动条的轨道,保留滑块可操作性,但视觉上不显示滚动条 */
+  /* ::-webkit-scrollbar-track {
   display: none;
 } */
 
-.text-container {
-  width: 100%;
-  overflow: hidden;
-  text-overflow: ellipsis;
+  .text-container {
+    width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
   }
 </style>