48 Commits 47b834e848 ... b8e2509ccd

Author SHA1 Message Date
  Mcal b8e2509ccd ✨ feat(header, menu, order): 调整组件布局,优化样式,更新搜索功能 4 days ago
  Mcal c4ff459075 ✨ feat(order): 优化订单组件布局,调整样式并添加桌子选择弹窗 4 days ago
  Mcal 568d9231ab Merge branch 'master' of http://1.94.207.143:3000/chongqing/store-project into lyz_dev 4 days ago
  陈雪 18ddfc365d Merge branch 'lyz_dev' 4 days ago
  陈雪 cf72c4ad0d Merge branch 'lyz_dev' 4 days ago
  陈雪 78156c7883 ✨ feat(platform): 添加平台检测功能 4 days ago
  陈雪 0d46eb152e Merge branch 'wl_dev' 5 days ago
  PIWALIN 18b1ce27a5 feat:商品页面整理 警告处理 5 days ago
  陈雪 a3d4d4dd8e ✨ feat(main): 优化主入口文件,移除未使用的图标组件和创建 Pinia 的方式 5 days ago
  陈雪 dad9b3dc09 Merge branch 'wl_dev' 5 days ago
  陈雪 f0d8b33c78 ✨ feat(user): 添加 LoginForm 接口并实现登录功能 6 days ago
  PIWALIN 14b7d09abf feat:样式修改 6 days ago
  陈雪 a3062c3d8a ✨ feat(request): 将请求选项中的 body 属性重命名为 data,以提高一致性 6 days ago
  陈雪 ca2f5d738c ✨ feat(login): 将登录按钮改为使用 el-button 组件并实现登录功能 6 days ago
  陈雪 4da8d4a8ef ✨ feat(request): 重构请求函数,优化类型定义和错误处理逻辑 6 days ago
  陈雪 8c15714f73 ✨ feat(capabilities): 精简 default.json 文件,优化窗口权限配置 6 days ago
  陈雪 8d57ba9c57 ✨ feat(login): 重构登录页面,添加响应式设计和密码可见性切换功能 6 days ago
  陈雪 16476675b9 ✨ feat(tauri): 调整窗口宽度以改善用户界面体验 6 days ago
  陈雪 d07998704e ✨ feat(menu): 优化 CapsuleBox 组件的代码格式和可读性 6 days ago
  陈雪 c7c1f02a3e ✨ feat(tauri): 添加单实例插件的窗口聚焦功能 6 days ago
  陈雪 759a6c2aba ✨ feat(tauri): 添加单实例插件以支持应用程序的单一运行实例 6 days ago
  陈雪 da055c25a4 ✨ feat(login): 重构登录页面,添加登录逻辑和背景图 6 days ago
  陈雪 308b88eac1 ✨ refactor(main): 优化应用挂载方式以提高可读性 6 days ago
  陈雪 b483f8b28a ✨ feat(store): 引入请求工具以增强用户存储功能 6 days ago
  陈雪 efa3280d30 ✨ feat(styles): 添加响应式单位转换函数pxToVw以支持视口宽度 6 days ago
  PIWALIN e49077f526 fix:弹窗功能的bug 修改 6 days ago
  陈雪 b9dc87dce4 ✨ feat(request): 增强请求功能,支持查询参数和可选身份验证 1 week ago
  陈雪 021ddc2d75 ✨ feat(upload): 添加上传插件并更新相关配置 1 week ago
  陈雪 4734f6ae43 ✨ feat(dependencies): 更新tauri插件版本并添加http请求工具 1 week ago
  陈雪 d59812b414 ✨ feat(store): 添加用户存储功能并集成tauri插件 1 week ago
  陈雪 18a0a06b53 ✨ feat(tauri): 添加tauri-plugin-store和tauri-plugin-http依赖并更新权限设置 1 week ago
  PIWALIN 71ff87e328 Merge branch 'master' into wl_dev 1 week ago
  PIWALIN 73391198e5 样式修改 1 week ago
  陈雪 346ecab114 Merge branch 'wl_dev' 1 week ago
  陈雪 b2ec0ef752 ✨ feat(test): 将local_logo字段更新为驼峰命名法 1 week ago
  陈雪 be65fd3d02 ✨ feat(test): 将本地Logo字段更新为驼峰命名法 1 week ago
  陈雪 8407c63126 ✨ feat(database): 将数据库表字段更新为驼峰命名法 1 week ago
  陈雪 1f741c25a5 ✨ feat(database): 更新数据库模型字段为驼峰命名法并添加Kysely插件 1 week ago
  PIWALIN 441d40b2f1 弹窗的静态页面 1 week ago
  陈雪 fa8c6b4dc6 ✨ feat(test): 添加描述字段到数据输入和显示 1 week ago
  陈雪 23a3217424 ✨ feat(database): 创建菜单分类、商品、SKU和规格表 1 week ago
  陈雪 6a56d6460f ✨ feat(database): 添加菜单相关数据表和更新测试页面 1 week ago
  陈雪 62a986e4c2 ✨ feat(database): 添加初始数据库迁移脚本和表结构定义 1 week ago
  陈雪 4684cea1c5 ✨ feat(test): 重构测试页面,添加数据增删改功能和模态框支持 1 week ago
  陈雪 09476155be 🦄 refactor(App): 移除未使用的脚本和样式,优化组件结构 1 week ago
  陈雪 b4aa59a68b ✨ feat(test): 添加测试页面和按钮功能,更新路由历史模式 1 week ago
  陈雪 3b023bb3c5 🦄 refactor(menu): 清理无用导入和注释,优化代码结构 1 week ago
  陈雪 b147519b02 🌈 style(indent): 修改缩进类型 1 week ago
52 changed files with 2246 additions and 267 deletions
  1. 1 0
      .prettierrc.cjs
  2. 1 0
      index.html
  3. 4 0
      package.json
  4. 40 0
      pnpm-lock.yaml
  5. 313 5
      src-tauri/Cargo.lock
  6. 7 0
      src-tauri/Cargo.toml
  7. 19 3
      src-tauri/capabilities/default.json
  8. 69 0
      src-tauri/resources/init.sql
  9. 33 7
      src-tauri/src/lib.rs
  10. 2 1
      src-tauri/tauri.conf.json
  11. 3 21
      src/App.vue
  12. BIN
      src/assets/img/bg_Icon.png
  13. 300 0
      src/assets/img/login/bg.svg
  14. 7 0
      src/assets/img/login/close-one.svg
  15. 9 0
      src/assets/img/login/earth.svg
  16. 6 0
      src/assets/img/login/open-one.svg
  17. 68 0
      src/assets/img/login/right.svg
  18. BIN
      src/assets/img/minus_Icon.png
  19. BIN
      src/assets/img/plus_Icon.png
  20. BIN
      src/assets/img/search_Icon.png
  21. 12 0
      src/assets/main.scss
  22. 60 0
      src/components/SearchInput.vue
  23. 25 0
      src/components/titleFood.vue
  24. 2 1
      src/config/db.ts
  25. 8 1
      src/i18n/en.json
  26. 7 2
      src/i18n/zh.json
  27. 5 0
      src/interface/user.ts
  28. 4 7
      src/main.ts
  29. 8 2
      src/model/index.ts
  30. 18 0
      src/model/menu_cate.ts
  31. 35 0
      src/model/menu_commodity.ts
  32. 19 0
      src/model/menu_sku.ts
  33. 14 0
      src/model/menu_spec.ts
  34. 0 11
      src/model/todo.ts
  35. 16 9
      src/router/index.ts
  36. 18 0
      src/store/index.ts
  37. 24 0
      src/store/user.ts
  38. 0 12
      src/stores/index.js
  39. 7 0
      src/utils/platform.ts
  40. 66 0
      src/utils/request.ts
  41. 13 0
      src/views/goods/index.vue
  42. 20 25
      src/views/layout/components/Header.vue
  43. 9 0
      src/views/layout/index.vue
  44. 167 3
      src/views/login/index.vue
  45. 75 0
      src/views/menu/components/CapsuleBox.vue
  46. 117 0
      src/views/menu/components/menuFood.vue
  47. 197 17
      src/views/menu/components/order-drawer.vue
  48. 7 5
      src/views/menu/components/rightOrder/components/order.vue
  49. 83 133
      src/views/menu/index.vue
  50. 7 2
      src/views/order/index.vue
  51. 312 0
      src/views/test/index.vue
  52. 9 0
      src/vite-env.d.ts

+ 1 - 0
.prettierrc.cjs

@@ -8,6 +8,7 @@ module.exports = {
   trailingComma: 'all',
   endOfLine: 'auto',
   htmlWhitespaceSensitivity: 'ignore',
+  vueIndentScriptAndStyle: true,
   overrides: [
     {
       files: '*.json',

+ 1 - 0
index.html

@@ -5,6 +5,7 @@
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>Tauri + Vue 3 App</title>
+    <script src="http://localhost:8098"></script>
     <!-- <link href="/src/styles.css" rel="stylesheet"> -->
   </head>
 

+ 4 - 0
package.json

@@ -18,8 +18,12 @@
     "@element-plus/icons-vue": "^2.3.1",
     "@tailwindcss/vite": "^4.0.8",
     "@tauri-apps/api": "^2",
+    "@tauri-apps/plugin-http": "~2",
     "@tauri-apps/plugin-opener": "^2",
+    "@tauri-apps/plugin-os": "~2",
     "@tauri-apps/plugin-sql": "^2.2.0",
+    "@tauri-apps/plugin-store": "~2",
+    "@tauri-apps/plugin-upload": "~2",
     "element-plus": "^2.9.5",
     "pinia": "^3.0.1",
     "tailwindcss": "^4.0.8",

+ 40 - 0
pnpm-lock.yaml

@@ -17,12 +17,24 @@ importers:
       '@tauri-apps/api':
         specifier: ^2
         version: 2.2.0
+      '@tauri-apps/plugin-http':
+        specifier: ~2
+        version: 2.3.0
       '@tauri-apps/plugin-opener':
         specifier: ^2
         version: 2.2.5
+      '@tauri-apps/plugin-os':
+        specifier: ~2
+        version: 2.2.0
       '@tauri-apps/plugin-sql':
         specifier: ^2.2.0
         version: 2.2.0
+      '@tauri-apps/plugin-store':
+        specifier: ~2
+        version: 2.2.0
+      '@tauri-apps/plugin-upload':
+        specifier: ~2
+        version: 2.2.1
       element-plus:
         specifier: ^2.9.5
         version: 2.9.5(vue@3.5.13(typescript@5.7.3))
@@ -531,12 +543,24 @@ packages:
     engines: {node: '>= 10'}
     hasBin: true
 
+  '@tauri-apps/plugin-http@2.3.0':
+    resolution: {integrity: sha512-pigTvz+zzAqbIhCzRiR1GE98Jw7A03j2V+Eiexr9thBI8VfMiwFQMcbgON51xlwnVaI72LdbYKNajU84im8tlg==}
+
   '@tauri-apps/plugin-opener@2.2.5':
     resolution: {integrity: sha512-hHsJ9RPWpZvZEPVFaL+d25gABMUMOf/A6ESXnvf/ii9guTukj58WXsAE/SOysXRIhej7kseRCxnOnIMpSCdUsQ==}
 
+  '@tauri-apps/plugin-os@2.2.0':
+    resolution: {integrity: sha512-HszbCdbisMlu5QhCNAN8YIWyz2v33abAWha6+uvV2CKX8P5VSct/y+kEe22JeyqrxCnWlQ3DRx7s49Byg7/0EA==}
+
   '@tauri-apps/plugin-sql@2.2.0':
     resolution: {integrity: sha512-yGdybpaMENe/p6lTXslvDHYNNvD9qB7palaBBF5fJHdYSkwd3vrLiYU9dFfLwUAwnsBylND55EiivWsjhazejA==}
 
+  '@tauri-apps/plugin-store@2.2.0':
+    resolution: {integrity: sha512-hJTRtuJis4w5fW1dkcgftsYxKXK0+DbAqurZ3CURHG5WkAyyZgbxpeYctw12bbzF9ZbZREXZklPq8mocCC3Sgg==}
+
+  '@tauri-apps/plugin-upload@2.2.1':
+    resolution: {integrity: sha512-2EyVhJYLAp2mJH0UzO3QGU0vPk/YWvAfdI2wXbczyzEZY/AZVa9wConyB1TV/NGhyJRim4LwWgkmnEvcKLkECw==}
+
   '@types/estree@1.0.6':
     resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
 
@@ -1370,14 +1394,30 @@ snapshots:
       '@tauri-apps/cli-win32-ia32-msvc': 2.2.7
       '@tauri-apps/cli-win32-x64-msvc': 2.2.7
 
+  '@tauri-apps/plugin-http@2.3.0':
+    dependencies:
+      '@tauri-apps/api': 2.2.0
+
   '@tauri-apps/plugin-opener@2.2.5':
     dependencies:
       '@tauri-apps/api': 2.2.0
 
+  '@tauri-apps/plugin-os@2.2.0':
+    dependencies:
+      '@tauri-apps/api': 2.2.0
+
   '@tauri-apps/plugin-sql@2.2.0':
     dependencies:
       '@tauri-apps/api': 2.2.0
 
+  '@tauri-apps/plugin-store@2.2.0':
+    dependencies:
+      '@tauri-apps/api': 2.2.0
+
+  '@tauri-apps/plugin-upload@2.2.1':
+    dependencies:
+      '@tauri-apps/api': 2.2.0
+
   '@types/estree@1.0.6': {}
 
   '@types/lodash-es@4.17.12':

+ 313 - 5
src-tauri/Cargo.lock

@@ -534,7 +534,7 @@ dependencies = [
  "bitflags 2.8.0",
  "block",
  "cocoa-foundation",
- "core-foundation",
+ "core-foundation 0.10.0",
  "core-graphics",
  "foreign-types",
  "libc",
@@ -549,7 +549,7 @@ checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d"
 dependencies = [
  "bitflags 2.8.0",
  "block",
- "core-foundation",
+ "core-foundation 0.10.0",
  "core-graphics-types",
  "libc",
  "objc",
@@ -592,11 +592,40 @@ version = "0.18.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
 dependencies = [
+ "percent-encoding",
  "time",
  "version_check",
 ]
 
 [[package]]
+name = "cookie_store"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
+dependencies = [
+ "cookie",
+ "document-features",
+ "idna",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
 name = "core-foundation"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -619,7 +648,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
 dependencies = [
  "bitflags 2.8.0",
- "core-foundation",
+ "core-foundation 0.10.0",
  "core-graphics-types",
  "foreign-types",
  "libc",
@@ -632,7 +661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
 dependencies = [
  "bitflags 2.8.0",
- "core-foundation",
+ "core-foundation 0.10.0",
  "libc",
 ]
 
@@ -776,6 +805,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "data-url"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
+
+[[package]]
 name = "der"
 version = "0.7.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -904,6 +939,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "document-features"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
 name = "dotenvy"
 version = "0.15.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -975,6 +1019,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
 
 [[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
 name = "endi"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1164,6 +1217,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
 name = "futures-channel"
 version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1249,6 +1317,7 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
 dependencies = [
+ "futures-channel",
  "futures-core",
  "futures-io",
  "futures-macro",
@@ -1379,6 +1448,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "gethostname"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30"
+dependencies = [
+ "rustix",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "getrandom"
 version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1569,6 +1648,25 @@ dependencies = [
 ]
 
 [[package]]
+name = "h2"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.7.1",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
+[[package]]
 name = "hashbrown"
 version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1708,6 +1806,7 @@ dependencies = [
  "bytes",
  "futures-channel",
  "futures-util",
+ "h2",
  "http",
  "http-body",
  "httparse",
@@ -2191,6 +2290,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
 
 [[package]]
+name = "litrs"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
+
+[[package]]
 name = "lock_api"
 version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2766,6 +2871,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "os_info"
+version = "3.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a604e53c24761286860eba4e2c8b23a0161526476b1de520139d69cdb85a6b5"
+dependencies = [
+ "log",
+ "serde",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "pango"
 version = "0.18.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3154,6 +3270,22 @@ dependencies = [
 ]
 
 [[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
+dependencies = [
+ "idna",
+ "psl-types",
+]
+
+[[package]]
 name = "quick-xml"
 version = "0.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3311,6 +3443,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
 
 [[package]]
+name = "read-progress-stream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6435842fc2fea44b528719eb8c32203bbc1bb2f5b619fbe0c0a3d8350fd8d2a8"
+dependencies = [
+ "bytes",
+ "futures",
+ "pin-project-lite",
+]
+
+[[package]]
 name = "redox_syscall"
 version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3378,8 +3521,12 @@ checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
 dependencies = [
  "base64 0.22.1",
  "bytes",
+ "cookie",
+ "cookie_store",
+ "encoding_rs",
  "futures-core",
  "futures-util",
+ "h2",
  "http",
  "http-body",
  "http-body-util",
@@ -3401,6 +3548,7 @@ dependencies = [
  "serde_json",
  "serde_urlencoded",
  "sync_wrapper",
+ "system-configuration",
  "tokio",
  "tokio-rustls",
  "tokio-util",
@@ -4128,8 +4276,13 @@ dependencies = [
  "serde_json",
  "tauri",
  "tauri-build",
+ "tauri-plugin-http",
  "tauri-plugin-opener",
+ "tauri-plugin-os",
+ "tauri-plugin-single-instance",
  "tauri-plugin-sql",
+ "tauri-plugin-store",
+ "tauri-plugin-upload",
 ]
 
 [[package]]
@@ -4234,6 +4387,36 @@ dependencies = [
 ]
 
 [[package]]
+name = "sys-locale"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "system-configuration"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
+dependencies = [
+ "bitflags 2.8.0",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
 name = "system-deps"
 version = "6.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4254,7 +4437,7 @@ checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9"
 dependencies = [
  "bitflags 2.8.0",
  "cocoa",
- "core-foundation",
+ "core-foundation 0.10.0",
  "core-graphics",
  "crossbeam-channel",
  "dispatch",
@@ -4432,6 +4615,51 @@ dependencies = [
 ]
 
 [[package]]
+name = "tauri-plugin-fs"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1edf18000f02903a7c2e5997fb89aca455ecbc0acc15c6535afbb883be223"
+dependencies = [
+ "anyhow",
+ "dunce",
+ "glob",
+ "percent-encoding",
+ "schemars",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "tauri",
+ "tauri-plugin",
+ "tauri-utils",
+ "thiserror 2.0.11",
+ "toml 0.8.20",
+ "url",
+ "uuid",
+]
+
+[[package]]
+name = "tauri-plugin-http"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a8137a106e0741fdd357366178fc6e0597abe7d20796f53f44171a1bcec1683"
+dependencies = [
+ "data-url",
+ "http",
+ "regex",
+ "reqwest",
+ "schemars",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "tauri-plugin-fs",
+ "thiserror 2.0.11",
+ "tokio",
+ "url",
+ "urlpattern",
+]
+
+[[package]]
 name = "tauri-plugin-opener"
 version = "2.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4454,6 +4682,39 @@ dependencies = [
 ]
 
 [[package]]
+name = "tauri-plugin-os"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738"
+dependencies = [
+ "gethostname",
+ "log",
+ "os_info",
+ "serde",
+ "serde_json",
+ "serialize-to-javascript",
+ "sys-locale",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.11",
+]
+
+[[package]]
+name = "tauri-plugin-single-instance"
+version = "2.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25bbc73eed15bba8ad290a52614f2711280df4bf575b36ce78f64367074b90b7"
+dependencies = [
+ "serde",
+ "serde_json",
+ "tauri",
+ "thiserror 2.0.11",
+ "tracing",
+ "windows-sys 0.59.0",
+ "zbus",
+]
+
+[[package]]
 name = "tauri-plugin-sql"
 version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4473,6 +4734,41 @@ dependencies = [
 ]
 
 [[package]]
+name = "tauri-plugin-store"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c0c08fae6995909f5e9a0da6038273b750221319f2c0f3b526d6de1cde21505"
+dependencies = [
+ "dunce",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.11",
+ "tokio",
+ "tracing",
+]
+
+[[package]]
+name = "tauri-plugin-upload"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e90ac6d3a783d4406caeae8c75aa05e96346474765517fddfd1dc313ff91aa89"
+dependencies = [
+ "futures-util",
+ "log",
+ "read-progress-stream",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.11",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
 name = "tauri-runtime"
 version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4703,10 +4999,22 @@ dependencies = [
  "mio",
  "pin-project-lite",
  "socket2",
+ "tokio-macros",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
+name = "tokio-macros"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.98",
+]
+
+[[package]]
 name = "tokio-rustls"
 version = "0.26.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"

+ 7 - 0
src-tauri/Cargo.toml

@@ -22,6 +22,13 @@ tauri = { version = "2", features = [] }
 tauri-plugin-opener = "2"
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
+tauri-plugin-store = "2"
+tauri-plugin-http = "2"
+tauri-plugin-upload = "2"
+tauri-plugin-os = "2"
 [dependencies.tauri-plugin-sql]
 features = ["sqlite"] # or "postgres", or "mysql"
 version = "2"
+
+[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
+tauri-plugin-single-instance = "2"

+ 19 - 3
src-tauri/capabilities/default.json

@@ -2,6 +2,22 @@
   "$schema": "../gen/schemas/desktop-schema.json",
   "identifier": "default",
   "description": "Capability for the main window",
-  "windows": ["main"],
-  "permissions": ["core:default", "opener:default", "sql:default", "sql:allow-execute"]
-}
+  "windows": [
+    "main"
+  ],
+  "permissions": [
+    "core:default",
+    "opener:default",
+    "sql:default",
+    "sql:allow-execute",
+    "store:default",
+    {
+      "identifier": "http:default",
+      "allow": [
+        "http://192.168.1.110:8080"
+      ]
+    },
+    "upload:default",
+    "os:default"
+  ]
+}

+ 69 - 0
src-tauri/resources/init.sql

@@ -0,0 +1,69 @@
+-- 创建菜单分类表
+CREATE TABLE
+    menu_cate (
+        id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键
+        name TEXT DEFAULT '', -- 分类名称
+        logo TEXT DEFAULT '', -- 分类Logo
+        local_logo TEXT 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 TABLE
+    menu_commodity (
+        id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键
+        type TEXT DEFAULT 'product', -- 商品类型
+        merchant_id INTEGER DEFAULT 0, -- 商户ID
+        store_id INTEGER DEFAULT 0, -- 店铺ID
+        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)
+    );
+
+-- 创建SKU表
+CREATE TABLE
+    menu_sku (
+        id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键
+        sku_no TEXT DEFAULT '', -- SKU编号
+        logo TEXT DEFAULT '', -- SKU Logo
+        goods_id INTEGER NOT NULL DEFAULT 0, -- 商品ID
+        spec_ids TEXT NOT NULL DEFAULT '', -- 规格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)
+    );
+
+-- 创建规格表
+CREATE TABLE
+    menu_spec (
+        id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键
+        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)
+    );

+ 33 - 7
src-tauri/src/lib.rs

@@ -1,15 +1,41 @@
-// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
-#[tauri::command]
-fn greet(name: &str) -> String {
-    format!("Hello, {}! You've been greeted from Rust!", name)
-}
+use std::vec;
+
+use tauri::{AppHandle, Manager};
+use tauri_plugin_sql::{Migration, MigrationKind};
 
 #[cfg_attr(mobile, tauri::mobile_entry_point)]
 pub fn run() {
+    let migrations = vec![Migration {
+        version: 1,
+        description: "create_initial_tables",
+        sql: include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/resources/init.sql")),
+        kind: MigrationKind::Up,
+    }];
     tauri::Builder::default()
-        .plugin(tauri_plugin_sql::Builder::new().build())
+        .plugin(tauri_plugin_os::init())
+        .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
+            let _ = show_window(app);
+        }))
+        .plugin(tauri_plugin_upload::init())
+        .plugin(tauri_plugin_http::init())
+        .plugin(tauri_plugin_store::Builder::new().build())
+        .plugin(
+            tauri_plugin_sql::Builder::default()
+                .add_migrations("sqlite:store.db", migrations)
+                .build(),
+        )
         .plugin(tauri_plugin_opener::init())
-        .invoke_handler(tauri::generate_handler![greet])
         .run(tauri::generate_context!())
         .expect("error while running tauri application");
 }
+
+fn show_window(app: &AppHandle) {
+    let windows = app.webview_windows();
+
+    windows
+        .values()
+        .next()
+        .expect("Sorry, no window found")
+        .set_focus()
+        .expect("Can't Bring Window to Focus");
+}

+ 2 - 1
src-tauri/tauri.conf.json

@@ -13,7 +13,7 @@
     "windows": [
       {
         "title": "盼达点餐",
-        "width": 1200,
+        "width": 1422,
         "height": 800
       }
     ],
@@ -29,6 +29,7 @@
   "bundle": {
     "active": true,
     "targets": "all",
+    "resources": ["resources/*"],
     "icon": [
       "icons/32x32.png",
       "icons/128x128.png",

+ 3 - 21
src/App.vue

@@ -1,26 +1,8 @@
-<script setup>
-import { ref, } from "vue";
-import { invoke } from "@tauri-apps/api/core";
-
-const greetMsg = ref("");
-const name = ref("");
-
-async function greet() {
-  // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
-  greetMsg.value = await invoke("greet", { name: name.value });
-}
-</script>
-
 <template>
   <RouterView />
 </template>
-
-<style scoped>
-
-</style>
 <style>
-.hide-bar::-webkit-scrollbar {
-  display: none;
-}
-
+  .hide-bar::-webkit-scrollbar {
+    display: none;
+  }
 </style>

BIN
src/assets/img/bg_Icon.png


File diff suppressed because it is too large
+ 300 - 0
src/assets/img/login/bg.svg


+ 7 - 0
src/assets/img/login/close-one.svg

@@ -0,0 +1,7 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Preview-close-one (&#233;&#162;&#132;&#232;&#167;&#136;-&#229;&#133;&#179;&#233;&#151;&#173;)">
+<path id="Vector" d="M6.16116 11.25C3.89911 13.125 2.5 15 2.5 15C2.5 15 8.09644 22.5 15 22.5C15.8562 22.5 16.6922 22.3846 17.5 22.1826M12.5199 7.8125C13.3215 7.6135 14.1509 7.5 15 7.5C21.9036 7.5 27.5 15 27.5 15C27.5 15 26.1009 16.875 23.8388 18.75" stroke="#333333" stroke-width="1.875" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_2" d="M12.6964 12.8887C12.1863 13.4448 11.875 14.1862 11.875 15.0004C11.875 16.7262 13.2741 18.1254 15 18.1254C15.8517 18.1254 16.6238 17.7846 17.1875 17.232" stroke="#333333" stroke-width="1.875" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_3" d="M26.25 26.25L3.75 3.75" stroke="#333333" stroke-width="1.875" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

+ 9 - 0
src/assets/img/login/earth.svg

@@ -0,0 +1,9 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Earth (&#229;&#156;&#176;&#231;&#144;&#131;&#228;&#187;&#170;)">
+<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M12 22C17.5229 22 22 17.5229 22 12C22 6.47715 17.5229 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5229 6.47715 22 12 22Z" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_2" d="M2 12H22" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_3" fill-rule="evenodd" clip-rule="evenodd" d="M12 22C14.2092 22 16 17.5229 16 12C16 6.47715 14.2092 2 12 2C9.79085 2 8 6.47715 8 12C8 17.5229 9.79085 22 12 22Z" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_4" d="M4.92871 5.07129C6.73836 6.88094 9.23836 8.00024 11.9998 8.00024C14.7612 8.00024 17.2612 6.88094 19.0709 5.07129" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path id="Vector_5" d="M19.0709 18.9289C17.2612 17.1193 14.7612 16 11.9998 16C9.23836 16 6.73836 17.1193 4.92871 18.9289" stroke="#333333" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+</svg>

+ 6 - 0
src/assets/img/login/open-one.svg

@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Preview-close-one (&#233;&#162;&#132;&#232;&#167;&#136;-&#229;&#133;&#179;&#233;&#151;&#173;)">
+<path id="Vector" d="M6.16116 11.25C3.89911 13.125 2.5 15 2.5 15C2.5 15 8.09644 22.5 15 22.5C15.8562 22.5 16.6922 22.3846 17.5 22.1826M12.5199 7.8125C13.3215 7.6135 14.1509 7.5 15 7.5C21.9036 7.5 27.5 15 27.5 15C27.5 15 26.1009 16.875 23.8388 18.75" stroke="#333333" stroke-width="1.875" stroke-linecap="round" stroke-linejoin="round"/>
+<circle id="Ellipse 61" cx="15" cy="15" r="3.06" stroke="#333333" stroke-width="1.88"/>
+</g>
+</svg>

File diff suppressed because it is too large
+ 68 - 0
src/assets/img/login/right.svg


BIN
src/assets/img/minus_Icon.png


BIN
src/assets/img/plus_Icon.png


BIN
src/assets/img/search_Icon.png


+ 12 - 0
src/assets/main.scss

@@ -0,0 +1,12 @@
+@use 'sass:math';
+
+$baseWidth: 1920;
+$baseHight: 1080;
+
+@function pxToVW($px) {
+  @return #{math.div($px, $baseWidth) * 100}+ vw;
+}
+
+@function pxToVH($px) {
+  @return #{math.div($px, $baseHight) * 100}+ vh;
+}

+ 60 - 0
src/components/SearchInput.vue

@@ -0,0 +1,60 @@
+<!-- 搜索框 -->
+<template>
+  <div class="search-container">
+    <input
+      type="text"
+      class="search-input"
+      :placeholder="$t('menu.spicyNoodles')"
+      v-model="searchQuery"
+    />
+    <div class="search-icon" @click="searchBtn">
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+const searchBtn=()=>{
+  console.log(111);
+}
+const searchQuery = ref('')
+</script>
+
+<style lang="scss" scoped>
+.search-container {
+  display: flex;
+  align-items: center;
+  width: 660px;
+  height: 60px;
+  border: 1px solid #E6E6E6;
+  border-radius: 12px 0 0 12px;
+  background-color: #fff;
+  margin-bottom: 15px;
+  .search-input {
+    width: 100%;
+    height: 40px;
+    border: none;
+    outline: none;
+    font-size: 16px;
+    padding: 0 10px;
+    border-radius: 30px;
+  }
+  .search-icon {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-left: 10px;
+    cursor: pointer;
+    width: 66px;
+    height: 66px;
+    background-image: url('../assets/img/search_Icon.png');
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: center;
+  }
+}
+// .search-input::placeholder {
+//   color: #999;
+// }
+</style>

+ 25 - 0
src/components/titleFood.vue

@@ -0,0 +1,25 @@
+<!-- 标题 -->
+<template>
+    <div :style="{ fontSize: fontSize,}" class="title">
+      <slot></slot>
+    </div>
+  </template>
+  <script>
+  export default {
+    name: 'Title',
+    props: {
+      fontSize: {
+        type: String,
+        default: '24px',  // 默认字体大小
+      }
+    }
+  }
+  </script>
+  <style scoped>
+ .title{
+    height: 50px;
+    line-height: 50px;
+    font-weight: 600;
+ }
+  </style>
+  

+ 2 - 1
src/config/db.ts

@@ -1,5 +1,5 @@
 import TauriDatabase from '@tauri-apps/plugin-sql'
-import { Kysely } from 'kysely'
+import { Kysely, CamelCasePlugin } from 'kysely'
 import { TauriDialect } from './kysely'
 import { Database } from '@/model'
 
@@ -7,4 +7,5 @@ export const DB = new Kysely<Database>({
   dialect: new TauriDialect({
     database: await TauriDatabase.load('sqlite:store.db'),
   }),
+  plugins: [new CamelCasePlugin()],
 })

+ 8 - 1
src/i18n/en.json

@@ -3,7 +3,14 @@
     "sidebar.menu": "Menu",
     "sidebar.order": "Order",
     "sidebar.exit":"Exit",
+    "sidebar.goods":"Goods",
+
     
     "menu.noProducts": "No products have been added yet",
-    "menu.pendingOrders":"List Of Pending Orders"
+    "menu.pendingOrders":"List Of Pending Orders",
+    "menu.search": "Search for products",
+    "menu.spicyNoodles": "Spicy Seasoned Seafood Noodles",
+    "menu.taste": "Taste",
+    "menu.type": "Type",
+    "menu.number": "Number"
 }

+ 7 - 2
src/i18n/zh.json

@@ -3,7 +3,12 @@
     "sidebar.menu": "点餐",
     "sidebar.order": "订单",
     "sidebar.exit":"退出",
-
+    "sidebar.goods":"商品",
     "menu.noProducts": "还没有添加菜品哦",
-    "menu.pendingOrders":"查看进行中的订单"
+    "menu.pendingOrders":"查看进行中的订单",
+    "menu.search": "搜索产品",
+    "menu.spicyNoodles": "辣味海鲜面",
+    "menu.taste": "口味",
+    "menu.type": "类型",
+    "menu.number": "数量"
 }

+ 5 - 0
src/interface/user.ts

@@ -0,0 +1,5 @@
+export interface LoginForm {
+  username: string
+  password: string
+  loginFrom: string
+}

+ 4 - 7
src/main.ts

@@ -3,14 +3,11 @@ import router from './router'
 import App from './App.vue'
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
-import * as ElementPlusIconsVue from '@element-plus/icons-vue'
-import { createPinia } from 'pinia'
+import pinia from '@/store'
 import i18n from './i18n'
 import './styles.css'
 import './element.scss'
 
-const pinia = createPinia()
-const app = createApp(App).use(pinia).use(ElementPlus).use(i18n).use(router).mount('#app')
-// for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
-//   ;(app as any).component(key, component)
-// }
+const app = createApp(App)
+app.use(pinia).use(ElementPlus).use(i18n).use(router)
+app.mount('#app')

+ 8 - 2
src/model/index.ts

@@ -1,5 +1,11 @@
-import { TodosTable } from './todo'
+import { MenuCateTable } from './menu_cate'
+import { MenuCommodityTable } from './menu_commodity'
+import { MenuSkuTable } from './menu_sku'
+import { MenuSpecTable } from './menu_spec'
 
 export interface Database {
-  todos: TodosTable
+  menuCate: MenuCateTable
+  menuCommodity: MenuCommodityTable
+  menuSku: MenuSkuTable
+  menuSpec: MenuSpecTable
 }

+ 18 - 0
src/model/menu_cate.ts

@@ -0,0 +1,18 @@
+import { Generated, Insertable, Selectable, Updateable } from 'kysely'
+
+// 菜单分类表
+export interface MenuCateTable {
+  id: Generated<number> // 主键
+  name: string // 分类名称
+  logo: string // 分类Logo
+  localLogo: string // 本地Logo路径
+  description?: string // 分类描述
+  createTime?: string // 创建时间
+  updateTime?: string // 更新时间
+  sort: number // 排序
+  status: 'A' | 'D' // 状态 (A-Active, D-Deleted)
+}
+
+export type MenuCate = Selectable<MenuCateTable>
+export type NewMenuCate = Insertable<MenuCateTable>
+export type MenuCateUpdate = Updateable<MenuCateTable>

+ 35 - 0
src/model/menu_commodity.ts

@@ -0,0 +1,35 @@
+import { Generated, Insertable, Selectable, Updateable } from 'kysely'
+
+// 商品表
+export interface MenuCommodityTable {
+  id: Generated<number> // 主键
+  type: string // 商品类型
+  merchantId: number // 商户ID
+  storeId: number // 店铺ID
+  name: string // 商品名称
+  cateId: number // 分类ID
+  goodsNo: string // 商品编号
+  isSingleSpec: 'Y' | 'N' // 是否单规格 (Y-Yes, N-No)
+  logo: string // 商品Logo
+  images: string // 商品图片
+  price: number // 价格
+  linePrice: number // 划线价
+  stock: number // 库存
+  weight: number // 重量
+  couponIds: string // 优惠券ID
+  serviceTime: number // 服务时间
+  initSale: number // 初始销量
+  salePoint: string // 卖点
+  canUsePoint: 'Y' | 'N' // 是否可用积分 (Y-Yes, N-No)
+  isMemberDiscount: 'Y' | 'N' // 是否会员折扣 (Y-Yes, N-No)
+  sort: number // 排序
+  description?: string // 商品描述
+  createTime?: string // 创建时间
+  updateTime?: string // 更新时间
+  operator?: string // 操作员
+  status: 'A' | 'D' // 状态 (A-Active, D-Deleted)
+}
+
+export type MenuCommodity = Selectable<MenuCommodityTable>
+export type NewMenuCommodity = Insertable<MenuCommodityTable>
+export type MenuCommodityUpdate = Updateable<MenuCommodityTable>

+ 19 - 0
src/model/menu_sku.ts

@@ -0,0 +1,19 @@
+import { Generated, Insertable, Selectable, Updateable } from 'kysely'
+
+// SKU表
+export interface MenuSkuTable {
+  id: Generated<number> // 主键
+  skuNo: string // SKU编号
+  logo: string // SKU Logo
+  goodsId: number // 商品ID
+  specIds: string // 规格ID
+  stock: number // 库存
+  price: number // 价格
+  linePrice: number // 划线价
+  weight: number // 重量
+  status: 'A' | 'D' // 状态 (A-Active, D-Deleted)
+}
+
+export type MenuSku = Selectable<MenuSkuTable>
+export type NewMenuSku = Insertable<MenuSkuTable>
+export type MenuSkuUpdate = Updateable<MenuSkuTable>

+ 14 - 0
src/model/menu_spec.ts

@@ -0,0 +1,14 @@
+import { Generated, Insertable, Selectable, Updateable } from 'kysely'
+
+// 规格表
+export interface MenuSpecTable {
+  id: Generated<number> // 主键
+  goodsId: number // 商品ID
+  name: string // 规格名称
+  value: string // 规格值
+  status: 'A' | 'D' // 状态 (A-Active, D-Deleted)
+}
+
+export type MenuSpec = Selectable<MenuSpecTable>
+export type NewMenuSpec = Insertable<MenuSpecTable>
+export type MenuSpecUpdate = Updateable<MenuSpecTable>

+ 0 - 11
src/model/todo.ts

@@ -1,11 +0,0 @@
-import { Generated, Insertable, Selectable, Updateable } from 'kysely'
-
-export interface TodosTable {
-  id: Generated<number>
-  title: string
-  desc: string
-}
-
-export type Todo = Selectable<TodosTable>
-export type NewTodo = Insertable<TodosTable>
-export type TodoUpdate = Updateable<TodosTable>

+ 16 - 9
src/router/index.ts

@@ -1,4 +1,4 @@
-import { createWebHashHistory, createRouter, RouteRecordRaw } from 'vue-router'
+import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
 
 import LayoutView from '../views/layout/index.vue'
 import LoginView from '../views/login/index.vue'
@@ -6,7 +6,7 @@ import MenuView from '../views/menu/index.vue'
 import OrderView from '../views/order/index.vue'
 import orderDetail from '../views/order/orderDetail.vue'
 import ExitView from '../views/exit/index.vue'
-import { path } from '@tauri-apps/api'
+import GoodsView from '../views/goods/index.vue'
 
 const routes: RouteRecordRaw[] = [
   { path: '/login', component: LoginView },
@@ -16,26 +16,33 @@ const routes: RouteRecordRaw[] = [
     redirect: '/menu',
     children: [
       { path: '/menu', component: MenuView },
-      { 
-        path: '/order', 
+      {
+        path: '/order',
         children: [
           {
-            path:'orderList',
+            path: 'orderList',
             component: OrderView,
           },
           {
-            path:'orderDetail',
+            path: 'orderDetail',
             component: orderDetail,
-          }
-        ]
+          },
+        ],
       },
       { path: '/exit', component: ExitView },
+      { path: '/goods', component: GoodsView },
     ],
   },
+  {
+    path: '/test',
+    name: 'Test',
+    component: () => import('@/views/test/index.vue'),
+  },
 ]
 
 const router = createRouter({
-  history: createWebHashHistory(),
+  history: createWebHistory(),
   routes,
 })
+
 export default router

+ 18 - 0
src/store/index.ts

@@ -0,0 +1,18 @@
+import { createPinia } from 'pinia'
+import { LazyStore } from '@tauri-apps/plugin-store'
+
+const lazyStore = new LazyStore('panda.json')
+const pinia = createPinia()
+pinia.use(({ store }) => {
+  const storeKey = `panda-${store.$id}`
+  store.$subscribe((_, state) => {
+    lazyStore.set(storeKey, JSON.stringify(state)).then(() => {
+      lazyStore.save()
+    })
+  })
+  lazyStore.get(storeKey).then((result: string) => {
+    if (result) store.$patch(JSON.parse(result))
+  })
+})
+
+export default pinia

+ 24 - 0
src/store/user.ts

@@ -0,0 +1,24 @@
+import { LoginForm } from '@/interface/user'
+import router from '@/router'
+import { request } from '@/utils/request'
+import { defineStore } from 'pinia'
+
+export const useUserStore = defineStore('user', {
+  state: () => ({
+    token: '',
+  }),
+  actions: {
+    async login(data: LoginForm) {
+      const { token } = await request<{ token: string }>({
+        url: '/backendApi/login/cashRegisterDoLogin',
+        method: 'POST',
+        data,
+        noAuth: true,
+      })
+      this.token = token
+    },
+  },
+  getters: {
+    getToken: ({ token }) => token,
+  },
+})

+ 0 - 12
src/stores/index.js

@@ -1,12 +0,0 @@
-import { defineStore } from 'pinia'
-import { ref } from 'vue'
-
-export const useStore = defineStore('panda', ()=> {
-    const count = ref(110)
-    const foods = ref([
-        {catgroy:'素菜',child:[]}
-    ])
-    return{
-        count
-    }
-})

+ 7 - 0
src/utils/platform.ts

@@ -0,0 +1,7 @@
+import { platform } from '@tauri-apps/plugin-os'
+
+const currentPlatform = platform()
+
+export function isMobile() {
+  return currentPlatform === 'android' || currentPlatform === 'ios'
+}

+ 66 - 0
src/utils/request.ts

@@ -0,0 +1,66 @@
+import { useUserStore } from '@/store/user'
+import { fetch } from '@tauri-apps/plugin-http'
+
+type CommonOption = {
+  url: string
+  query?: Record<string, string>
+  noAuth?: boolean
+  headers?: Record<string, string>
+}
+
+type GetOption = {
+  method: 'GET' | 'DELETE'
+}
+
+type PostOption = {
+  method: 'POST' | 'PUT'
+  data?: unknown
+}
+
+type RequestOptions = CommonOption & (GetOption | PostOption)
+
+type Response<T> = {
+  code: number
+  data: T
+  msg: string
+}
+
+export const request = async <T = void>(config: RequestOptions) => {
+  const { noAuth, url, headers, method, query } = config
+  if (!noAuth) {
+    const { getToken } = useUserStore()
+    headers['Access-Token'] = getToken
+  }
+  const reqPath = new URL(url, import.meta.env.VITE_BASE_URL || '')
+  if (query) Object.keys(query).forEach((key) => reqPath.searchParams.append(key, query[key]))
+
+  try {
+    console.log('reqPath.href: ', reqPath.href)
+    let body = null
+    if (config.method === 'POST' || (config.method === 'PUT' && config.data)) {
+      body = JSON.stringify(config.data)
+    }
+
+    const response = await fetch(reqPath.href, {
+      method,
+      headers: {
+        'Content-Type': 'application/json',
+        ...headers,
+      },
+      body,
+    })
+
+    if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`)
+    }
+
+    const { code, data, msg } = (await response.json()) as Response<T>
+    if (code !== 200) {
+      throw new Error(msg)
+    }
+    return data
+  } catch (error) {
+    console.error('Request failed', error)
+    throw error
+  }
+}

+ 13 - 0
src/views/goods/index.vue

@@ -0,0 +1,13 @@
+<template>
+    <div>
+11111
+    </div>
+</template>
+
+<script setup>
+
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 20 - 25
src/views/layout/components/Header.vue

@@ -1,15 +1,16 @@
 <template>
   <header
-    class="flex h-[100px] items-center px-[48px] border-b-[1px] border-b-[#e6e6e6] justify-between"
+    class="flex h-[60px] items-center px-[48px] border-b-[1px] border-b-[#e6e6e6] justify-between"
   >
-    <div @click="execSQLTest" class="flex items-center">
-      <img class="w-[156px] mr-[10px]" src="/imgs/logo.svg" alt="" />
+    <div class="flex items-center">
+      <img class="w-[100px] mr-[10px]" src="/imgs/logo.svg" alt="" />
       <el-input
         v-model="keword"
         :input-style="{ boxShadow: 'none' }"
         style="width: 240px"
         placeholder="Please input"
         :prefix-icon="Search"
+        @click="show = true"
       />
     </div>
     <div class="flex items-center coursor-pointer">
@@ -37,28 +38,22 @@
       </div>
     </div>
   </header>
+  <OrderDrawer v-model:show="show" :type="SearchType" />
+
 </template>
 <script lang="ts" setup>
-import { computed, ref } from 'vue'
-import { Search } from '@element-plus/icons-vue'
-import { messages } from '../../../i18n/index'
-import { DB } from '@/config/db'
-import { useI18n } from 'vue-i18n'
-const { t, locale } = useI18n()
-const langs = computed(() => Object.keys(messages))
-const curLang = computed(() => locale.value)
-const keword = ref('')
-function changeLanguage(lang) {
-  locale.value = lang
-}
-
-async function execSQLTest() {
-  DB.insertInto('todos')
-    .values({
-      title: '标题1',
-    })
-    .executeTakeFirst()
-  const result = await DB.selectFrom('todos').selectAll().where('id', '=', 1).execute()
-  console.log('resulr: ', result)
-}
+  import { computed, ref } from 'vue'
+  import { Search } from '@element-plus/icons-vue'
+  import { messages } from '../../../i18n/index'
+  import { useI18n } from 'vue-i18n'
+  import OrderDrawer from '../../menu/components/order-drawer.vue'
+  const { t, locale } = useI18n()
+  const langs = computed(() => Object.keys(messages))
+  const curLang = computed(() => locale.value)
+  const keword = ref('')
+  const show = ref(false)
+  const SearchType = ref('Search')
+  function changeLanguage(lang) {
+    locale.value = lang
+  }
 </script>

+ 9 - 0
src/views/layout/index.vue

@@ -21,6 +21,8 @@ import Header from './components/Header.vue'
 import menuCooker1 from '/imgs/menu-cooker1.png'
 import menuCooker2 from '/imgs/menu-cooker2.png'
 import menuCooker3 from '/imgs/menu-cooker3.png'
+// import menuCooker3 from '/imgs/menu-cooker3.png'
+
 import { useStoreI18n } from '@/hook/i18n'
 const { t, locale } = useI18n()
 const { $t } = useStoreI18n()
@@ -39,11 +41,18 @@ const menus = ref([
     path: '/order/orderList',
   },
   {
+    name: $t('sidebar.goods'),
+    img: menuCooker3,
+    key: 4,
+    path: '/goods',
+  },
+  {
     name: $t('sidebar.exit'),
     img: menuCooker3,
     key: 3,
     path: '/exit',
   },
+
 ])
 function handleClick(m) {
   const newMenus = menus.value.map((i) => {

+ 167 - 3
src/views/login/index.vue

@@ -1,5 +1,169 @@
 <template>
-    <div>
-        <h2>登录页 </h2>
+  <div class="login-container">
+    <div class="form-container">
+      <img src="@/assets/img/login/right.svg" alt="" class="right-img" />
+      <div class="login-form">
+        <div class="title">Hi,welcome back</div>
+        <div class="sumary-title">Cashier account</div>
+        <el-form ref="formRef" :model="loginForm">
+          <el-form-item prop="username" :rules="[]">
+            <el-input v-model="loginForm.username" />
+          </el-form-item>
+          <el-form-item prop="password" :rules="[]">
+            <el-input v-model="loginForm.password" :type="inputType">
+              <template #suffix>
+                <img
+                  v-if="inputType === 'password'"
+                  src="@/assets/img/login/close-one.svg"
+                  @click="inputType = ''"
+                />
+                <img @click="inputType = 'password'" v-else src="@/assets/img/login/open-one.svg" />
+              </template>
+            </el-input>
+          </el-form-item>
+        </el-form>
+        <div class="remember-password">
+          <el-switch v-model="isRremember" />
+          <div class="remember-text">remember password</div>
+        </div>
+        <el-button class="login-btn" @click="loginHandler" type="primary">log on</el-button>
+      </div>
     </div>
-</template>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { useUserStore } from '@/store/user'
+  import { reactive, ref, useTemplateRef } from 'vue'
+  const loginFormRef = useTemplateRef('formRef')
+
+  const inputType = ref('password')
+  const isRremember = ref(false)
+
+  const loginForm = reactive({
+    username: 'cheshidp',
+    password: '123456',
+    loginFrom: 'cashier',
+  })
+
+  const { login } = useUserStore()
+  const loginHandler = async () => {
+    await login(loginForm)
+  }
+</script>
+
+<style scoped lang="scss">
+  @use '@/assets/main.scss' as *;
+
+  .login-container {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 100vh;
+    width: 100vw;
+    background: url('@/assets/img/login/bg.svg') no-repeat center center;
+    background-size: cover;
+    box-sizing: border-box;
+
+    .form-container {
+      width: pxToVW(1600);
+      height: pxToVW(900);
+      flex-shrink: 0;
+      border-radius: 20px;
+      background: #fff;
+      overflow: hidden;
+      display: flex;
+      box-sizing: border-box;
+      padding: 0 pxToVW(102);
+      justify-content: space-between;
+
+      .login-form {
+        width: pxToVW(544);
+        box-sizing: border-box;
+        padding-top: pxToVH(229);
+
+        .title {
+          color: #000;
+          font-family: 'PingFang SC';
+          font-size: 40px;
+          font-style: normal;
+          font-weight: 600;
+          line-height: normal;
+          text-transform: capitalize;
+          margin-bottom: pxToVH(50);
+        }
+
+        .sumary-title {
+          color: #333;
+          font-family: 'PingFang SC';
+          font-size: 20px;
+          font-style: normal;
+          font-weight: 500;
+          line-height: normal;
+          text-transform: capitalize;
+          margin-bottom: pxToVH(8);
+        }
+
+        :deep(.el-form-item) {
+          --el-fill-color-blank: #f6f4f4;
+          .el-input {
+            height: pxToVH(64);
+          }
+
+          &:first-child {
+            margin-bottom: pxToVH(30);
+          }
+        }
+
+        .remember-password {
+          display: flex;
+          align-items: center;
+          margin-bottom: pxToVH(24);
+
+          .remember-text {
+            display: block;
+            color: #1a1a1a;
+            font-feature-settings: 'liga' off, 'clig' off;
+            font-family: 'PingFang SC';
+            font-size: 14px;
+            font-style: normal;
+            font-weight: 400;
+            line-height: 22px; /* 157.143% */
+            letter-spacing: 0.3px;
+            text-transform: capitalize;
+            margin-left: pxToVW(8);
+          }
+        }
+
+        .login-btn {
+          display: flex;
+          width: 100%;
+          height: 54px;
+          padding: 10px;
+          justify-content: center;
+          align-items: center;
+          gap: 10px;
+          flex-shrink: 0;
+          border-radius: 8px;
+          background: #f67f20;
+          color: #fff;
+          font-family: 'PingFang SC';
+          font-size: 20px;
+          font-style: normal;
+          font-weight: 500;
+          line-height: normal;
+        }
+
+        img {
+          cursor: pointer;
+          width: 30px;
+        }
+      }
+
+      .right-img {
+        width: calc(100% - pxToVW(763));
+        flex-shrink: 0;
+      }
+    }
+  }
+</style>

+ 75 - 0
src/views/menu/components/CapsuleBox.vue

@@ -0,0 +1,75 @@
+<!-- 胶囊 -->
+<template>
+  <div class="capsule-container">
+    <div
+      class="capsule-box"
+      v-for="(item, index) in items"
+      :key="index"
+      @click="changeColor(index, item)"
+      :class="['tag-button', { active: clickedIndex === index }]"
+    >
+      {{ item.text }}
+    </div>
+  </div>
+</template>
+
+<script setup>
+  import { ref } from 'vue'
+  const props = defineProps({
+    items: {
+      type: Array,
+      required: true,
+    },
+  })
+  // 使用 ref 来追踪哪个盒子被点击
+  const clickedIndex = ref(null)
+  // 点击事件函数
+  const changeColor = (index, item) => {
+    // 如果点击的是已选中的盒子,取消选中状态,否则设置为选中
+    if (clickedIndex.value === index) {
+      clickedIndex.value = null
+    } else {
+      clickedIndex.value = index // 选中当前盒子
+      console.log(index, item.text, '1111')
+    }
+  }
+</script>
+
+<style scoped lang="scss">
+  .capsule-container {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 16px;
+  }
+  .capsule-box {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 0.5rem;
+  }
+
+  .tag-button {
+    padding: 0.5rem 1.5rem;
+    font-size: 0.875rem;
+    transition: all 0.3s;
+    white-space: nowrap;
+    border: 1px solid #e5e7eb;
+    background-color: white;
+    color: #4b5563;
+    border-radius: 0.375rem;
+  }
+
+  .tag-button:hover {
+    background-color: #f67f20;
+    color: #fff;
+  }
+
+  .tag-button.active {
+    background-color: #f97316;
+    color: white;
+    border-color: #f97316;
+  }
+
+  .tag-button.active:hover {
+    background-color: #ea580c;
+  }
+</style>

+ 117 - 0
src/views/menu/components/menuFood.vue

@@ -0,0 +1,117 @@
+<!-- 菜品 -->
+<template>
+  <!-- <div class="food_warp"> -->
+    <div class="food_container" @click="food_btnNumber">
+      <div class="item">
+        <img :src="imageUrl" alt="Food Image" />
+      </div>
+      <div class="w-[320px] h-[80px] flex items-center">
+        {{ productName }}
+      </div>
+      <div class="item">{{ price }}</div>
+      <div class="item">{{ quantity }}</div>
+    </div>
+    <div class="w-[398px] h-[102px]" v-show="showNumberClose">
+      <Title fontSize="16px">Number</Title>
+      <div class="food_number">
+        <div @click="handleChange(-1)">
+          <img src="../../../assets//img//minus_Icon.png" alt="" />
+        </div>
+        <div class="numItem">{{ num }}</div>
+        <div @click="handleChange(1)">
+          <img src="../../../assets//img//plus_Icon.png" alt="" />
+        </div>
+      </div>
+    </div>
+  <!-- </div> -->
+</template>
+
+<script setup>
+  import Title from '@/components/titleFood.vue'
+  import { ref } from 'vue'
+  const props = defineProps({
+    imageUrl: {
+      type: String,
+      required: true,
+    },
+    productName: {
+      type: String,
+      required: true,
+    },
+    price: {
+      type: String,
+      required: true,
+    },
+    quantity: {
+      type: String,
+      required: true,
+    },
+    type: {
+      type: String,
+      required: true,
+    },
+  })
+  const num = ref(0)
+  const showNumberClose = ref(false)
+  // 计数器
+  const handleChange = (value) => {
+    if (num.value > 0 || value > 0) {
+      num.value += value
+    }
+  }
+  // 判断计数器状态
+  const food_btnNumber = () => {
+    if (props.type === 'Search') {
+      if (showNumberClose.value) {
+        showNumberClose.value = false
+      } else {
+        showNumberClose.value = true
+      }
+    }
+  }
+</script>
+<style lang="scss" scoped>
+  // .food_warp {
+  //   display: flex;
+  //   flex-wrap: wrap;
+  //   justify-content: space-between;
+  //   align-items: center;
+  // }
+  .food_container {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    gap: 16px;
+    background-color: #ffffff;
+    border-radius: 12px;
+    transition: all 0.3s ease;
+    .item {
+      width: 100px;
+      img {
+        width: 64px;
+        height: 64px;
+        border-radius: 50%;
+      }
+    }
+  }
+  .food_container .item:nth-child(3),
+  .food_container .item:nth-child(4) {
+    text-align: end;
+  }
+  .food_number {
+    display: flex;
+    align-items: center;
+  }
+  .numItem {
+    width: 40px;
+    height: 40px;
+    border-radius: 8px;
+    margin: 0px 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #000;
+    font-size: 20px;
+    border: 1px solid #ccc;
+  }
+</style>

+ 197 - 17
src/views/menu/components/order-drawer.vue

@@ -1,22 +1,202 @@
 <template>
-  <el-drawer v-model="show" :show-close="false" direction="rtl" size="700px">
-    <template #header>
-      <div>
-        <h1 class="">Search for products</h1>
-        <el-divider />
-      </div>
-    </template>
-    <template #footer>
-      <div style="flex: auto">
-        <el-button type="primary" @click="cancelClick" plain>close window</el-button>
-        <el-button type="primary" @click="confirmClick">Join the checkout counter</el-button>
-      </div>
-    </template>
-  </el-drawer>
+  <div>
+    <el-drawer v-model="show" :show-close="false" direction="rtl" size="700px">
+      <template #header>
+        <div class="food_title">
+          {{ $t('menu.search') }}
+        </div>
+      </template>
+
+      <template #default>
+        <div class="scroll">
+          <SearchInput></SearchInput>
+          <!-- 判断从那点击进来 Search点击搜索框 -->
+          <div v-if="type === 'Search'">
+            <!-- 判断foodItems 显示图片 -->
+            <div v-if="foodItems.length === 0" class="search_product"></div>
+            <div class="food_img" v-else>
+              <menuFood
+                v-for="(item, index) in foodItems"
+                :key="index"
+                :imageUrl="item.image"
+                :productName="item.description"
+                :price="item.price"
+                :quantity="item.quantity"
+                :type="type"
+              />
+            </div>
+          </div>
+          <!-- 从菜品 -->
+          <div v-else>
+            <div>
+              <el-divider />
+              <menuFood
+                v-for="(item, index) in foodSearch"
+                :key="index"
+                :imageUrl="item.image"
+                :productName="item.description"
+                :price="item.price"
+                :quantity="item.quantity"
+                :type="type"
+              />
+              <Title fontSize="16px"> {{ $t('menu.taste') }}</Title>
+              <CapsuleBox :items="foodList" />
+              <Title fontSize="16px">{{ $t('menu.type') }}</Title>
+              <CapsuleBox :items="foodList" />
+              <Title fontSize="16px">{{ $t('menu.number') }}</Title>
+              <div class="flex items-center">
+                <div @click="handleChange(-1)">
+                   <img src="../../../assets//img//minus_Icon.png" alt="">
+                </div>
+                <div class="numItem">{{ num }}</div>
+                <div @click="handleChange(1)">
+                  <img src="../../../assets//img//plus_Icon.png" alt="">
+                </div>
+              </div>
+              <el-divider />
+              <menuFood
+                v-for="(item, index) in foodItems"
+                :key="index"
+                :imageUrl="item.image"
+                :productName="item.description"
+                :price="item.price"
+                :quantity="item.quantity"
+                :type="type"
+              />
+            </div>
+          </div>
+        </div>
+      </template>
+
+      <template #footer>
+        <div v-if="type === 'Search'">
+          <el-button style="height: 44px;" type="primary" class="w-[660px] h-[68px]" @click="cancelClick" plain>
+            close window
+          </el-button>
+        </div>
+        <div style="display: flex; justify-content: space-between; " v-else>
+          <el-button style="width: 220px; height: 44px;" type="primary" @click="cancelClick" plain>close window</el-button>
+          <el-button style="width: 408px; height: 44px" type="primary" @click="confirmClick">Join the checkout counter</el-button>
+        </div>
+      </template>
+    </el-drawer>
+  </div>
 </template>
+
 <script setup>
-const show = defineModel('show')
-const showInput = defineModel('showInput')
+  import { ref } from 'vue'
+  import SearchInput from '../../../components/SearchInput.vue'
+  import Title from '@/components/titleFood.vue'
+  import menuFood from './menuFood.vue'
+  import CapsuleBox from './CapsuleBox.vue'
+  const props = defineProps({
+    type: {
+      type: String,
+      required: true,
+    },
+  })
+  const show = defineModel('show')
+  const showInput = defineModel('showInput')
+  const num = ref(1)
+  // 胶囊
+  const foodList = ref([
+    { id: 1, text: 'sautéed products' },
+    { id: 2, text: 'sautéed Search' },
+    { id: 3, text: 'show' },
+    { id: 4, text: 'https for' },
+    { id: 5, text: 'sautéed' },
+    { id: 6, text: 'fjords for' },
+  ])
+  // 假数据,用于动态渲染菜品
+  const foodSearch = ref([
+  {
+    id: 1,
+    name: 'Super Unbeatable Delicious Seasonal Vegetables',
+    description: 'Fresh seasonal vegetables served with perfectly poached egg',
+    price: 1.29,
+    quantity: 5,
+    image: 'https://ai-public.mastergo.com/ai/img_res/dc344e2119e1987d040e75eade956be7.jpg'
+  },
+  {
+    id: 2,
+    name: 'Mediterranean Quinoa Bowl',
+    description: 'Protein-rich quinoa with roasted vegetables and tahini dressing',
+    price: 2.49,
+    quantity: 3,
+    image: 'https://ai-public.mastergo.com/ai/img_res/6ea381439b7a94e5b05754077c9b15c7.jpg'
+  },
+  ])
+  const foodItems = ref([
+  {
+    id: 1,
+    name: 'Super Unbeatable Delicious Seasonal Vegetables',
+    description: 'Fresh seasonal vegetables served with perfectly poached egg',
+    price: 1.29,
+    quantity: 5,
+    image: 'https://ai-public.mastergo.com/ai/img_res/dc344e2119e1987d040e75eade956be7.jpg'
+  },
+  {
+    id: 2,
+    name: 'Mediterranean Quinoa Bowl',
+    description: 'Protein-rich quinoa with roasted vegetables and tahini dressing',
+    price: 2.49,
+    quantity: 3,
+    image: 'https://ai-public.mastergo.com/ai/img_res/6ea381439b7a94e5b05754077c9b15c7.jpg'
+  },
+  {
+    id: 3,
+    name: 'Asian Fusion Noodle Bowl',
+    description: 'Hand-pulled noodles with seasonal vegetables in aromatic broth',
+    price: 3.99,
+    quantity: 2,
+    image: 'https://ai-public.mastergo.com/ai/img_res/ee655fefd4868fff86a1f8cc6975ea66.jpg'
+  }
+  ])
+  const cancelClick = () => {
+    show.value = false
+  }
+  const handleChange = (value) => {
+    if (num.value > 0 || value > 0) {
+      num.value += value
+    }
+  }
 </script>
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+  :deep(.el-drawer__header) {
+    margin-bottom: 0px !important;
+  }
+
+  :deep(.el-divider) {
+    margin: 20px 0px 15px 0px;
+  }
+  :deep( .el-drawer__body::-webkit-scrollbar ){
+    display: none;
+  }
+  .food_title {
+    height: 56px;
+    font-size: 24px;
+    font-weight: 600;
+    border-bottom: 1px solid #e6e6e6;
+  }
+  .search_product {
+    width: 660px;
+    height: 400px;
+    background-image: url('../../../assets/img/bg_Icon.png');
+    background-size: contain;
+    background-repeat: no-repeat;
+    background-position: center;
+  }
+  .numItem {
+    width: 40px;
+    height: 40px;
+    border-radius: 8px;
+    margin: 0px 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #000;
+    font-size: 20px;
+    border: 1px solid #ccc;
+  }
+</style>

+ 7 - 5
src/views/menu/components/rightOrder/components/order.vue

@@ -2,13 +2,13 @@
 
 <template>
   <div class="h-full bg-gray-50">
-    <div class="w-full mx-auto">
-      <div class="bg-white rounded-lg p-3">
+    <div class="w-full mx-auto h-full">
+      <div class="bg-white rounded-lg p-3 h-full">
         <div class="flex items-center justify-between mb-3">
           <h1 class="text-sm font-medium">Orders #45892</h1>
         </div>
-
-        <div class="space-y-4 mb-6 overflow-auto h-[200px]">
+        <div class="flex flex-col h-[calc(100%-3rem)]">
+          <div class="space-y-4 mb-6 overflow-auto flex-1">
           <div
             v-for="(item, index) in orderItems"
             :key="index"
@@ -59,6 +59,8 @@
             <span class="text-sm font-medium">${{ total.toFixed(2) }}</span>
           </div>
         </div>
+        </div>
+
       </div>
     </div>
   </div>
@@ -94,7 +96,7 @@ const orderItems = ref<OrderItem[]>([
     image: 'https://ai-public.mastergo.com/ai/img_res/a20aaccbc775fd5729c8b8c6dac46563.jpg',
   },
 ])
-
+orderItems.value.concat(orderItems.value)
 const total = computed(() => {
   return orderItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
 })

+ 83 - 133
src/views/menu/index.vue

@@ -8,7 +8,6 @@
               class="flex-1 h-full pr-[10px] flex items-center justify-between cursor-pointer overflow-hidden"
             >
               <div
-                @click="handleLeftClick"
                 class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]"
               >
                 <el-icon>
@@ -22,7 +21,6 @@
               >
                 <div ref="" class="flex flex-nowrap items-center">
                   <div
-                    @click="catgroyClick"
                     v-for="item in products"
                     class="flex items-center shrink-0 mr-[20px] h-[34px]"
                   >
@@ -31,7 +29,6 @@
                 </div>
               </div>
               <div
-                @click="handleRightClick"
                 class="text-[#999] shrink-0 flex items-center justify-center h-[34px] w-[44px] border border-[#e6e6e6] rounded-[12px]"
               >
                 <el-icon>
@@ -74,143 +71,96 @@
     </div>
     <OrderDrawer v-model:show="show" />
     <deskDrawer v-model:show="deskShow" />
+
   </div>
 </template>
-<script setup>
-import { ref, onMounted, nextTick } from 'vue'
-import { useStore } from '../../stores'
-import { storeToRefs } from 'pinia'
-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'
-const { t } = useI18n()
-const store = useStore()
-const { count } = storeToRefs(store)
-const scrollRef = ref(null)
-// function handleRightClick() {
-//  scrollRef.value.scrollLeft += 100
-// }
-
-// 计算元素宽度
-const calculateItemWidth = () => {
-  nextTick(() => {
-    const element = scrollRef.value?.children[0]
-    if (element) {
-      const style = window.getComputedStyle(element)
-      itemWidth.value = element.offsetWidth + parseInt(style.marginRight)
-    }
-  })
-}
-// function handleRightClick() {
-//   scrollRef.value.scrollLeft += 100
-// }
-
-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',
-  },
-])
-// 生命周期钩子
-onMounted(() => {
-  calculateItemWidth()
-})
+<script lang="ts" 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'
+  const { t } = useI18n()
+  const scrollRef = ref(null)
 
-// 左按钮点击
-const handleLeftClick = () => {
-  if (currentIndex.value <= 0) return
-  currentIndex.value--
-  scrollToPosition()
-}
+  const show = ref(false)
 
-// 右按钮点击
-const handleRightClick = () => {
-  // 假设products通过props传入
-  if (currentIndex.value >= products.value.length - 1) return
-  currentIndex.value++
-  scrollToPosition()
-}
+  onMounted(() => {})
 
-// 执行滚动
-const scrollToPosition = () => {
-  scrollRef.value?.scrollTo({
-    left: itemWidth.value * currentIndex.value,
-    behavior: 'smooth',
-  })
-}
+  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 deskShow = ref(false)
+  const catgroyClick = () => {
+    deskShow.value = true
+  }
 </script>
 <style scoped>
-::-webkit-scrollbar-thumb {
-  background-color: red;
-}
-/* 隐藏整个滚动条 */
-::-webkit-scrollbar {
-  display: none;
-}
+  ::-webkit-scrollbar-thumb {
+    background-color: red;
+  }
+  /* 隐藏整个滚动条 */
+  ::-webkit-scrollbar {
+    display: none;
+  }
 
-/* 或者仅隐藏滚动条的轨道,保留滑块可操作性,但视觉上不显示滚动条 */
-::-webkit-scrollbar-track {
-  display: none;
-}
-.text-container {
-  width: 100%;
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
+  /* 或者仅隐藏滚动条的轨道,保留滑块可操作性,但视觉上不显示滚动条 */
+  ::-webkit-scrollbar-track {
+    display: none;
+  }
+  .text-container {
+    width: 100%;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
 </style>

+ 7 - 2
src/views/order/index.vue

@@ -6,8 +6,8 @@
       <!-- Search Area -->
       <div class="mb-6 flex items-center gap-4">
         <el-input v-model="searchKeyword" placeholder="Enter Keywords To Search" class="!rounded-button">
-          <template #append>
-            <el-icon>
+          <template #append >
+            <el-icon class="w-full h-full" @click="console.log('搜索')">
               <Search />
             </el-icon>
           </template>
@@ -161,4 +161,9 @@ const handleDetails = (row) => {
   --el-table-border-color: #e5e7eb;
   --el-table-header-text-color: #4b5563;
 }
+.el-input{
+  --el-fill-color-light:#f67f20;
+  --el-color-info:white;
+  --el-input-border-color:#f67f20;
+}
 </style>

+ 312 - 0
src/views/test/index.vue

@@ -0,0 +1,312 @@
+<template>
+  <div class="container">
+    <div class="form-container">
+      <input v-model="newData.name" placeholder="输入名称" />
+      <input v-model="newData.logo" placeholder="输入Logo URL" />
+      <input v-model="newData.localLogo" placeholder="输入本地Logo路径" />
+      <input v-model="newData.sort" type="number" placeholder="输入排序" />
+      <input v-model="newData.description" placeholder="输入描述" />
+      <select v-model="newData.status">
+        <option value="A">Active</option>
+        <option value="D">Deleted</option>
+      </select>
+      <button @click="addData">添加数据</button>
+    </div>
+    <div class="json-container">
+      <table>
+        <thead>
+          <tr>
+            <th>ID</th>
+            <th>名称</th>
+            <th>Logo</th>
+            <th>本地Logo</th>
+            <th>排序</th>
+            <th>描述</th>
+            <th>状态</th>
+            <th>操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr v-for="item in jsonData" :key="item.id">
+            <td>{{ item.id }}</td>
+            <td>{{ item.name }}</td>
+            <td>{{ item.logo }}</td>
+            <td>{{ item.localLogo }}</td>
+            <td>{{ item.sort }}</td>
+            <td>{{ item.description }}</td>
+            <td>{{ item.status }}</td>
+            <td>
+              <button class="edit-button" @click="openEditModal(item)">修改</button>
+              <button class="delete-button" @click="deleteData(item.id)">删除</button>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div v-if="showModal" class="modal">
+      <div class="modal-content">
+        <span class="close" @click="closeModal">&times;</span>
+        <h2>编辑数据</h2>
+        <input v-model="editData.name" placeholder="输入名称" />
+        <input v-model="editData.logo" placeholder="输入Logo URL" />
+        <input v-model="editData.localLogo" placeholder="输入本地Logo路径" />
+        <input v-model="editData.sort" type="number" placeholder="输入排序" />
+        <input v-model="editData.description" placeholder="输入描述" />
+        <select v-model="editData.status">
+          <option value="A">Active</option>
+          <option value="D">Deleted</option>
+        </select>
+        <button @click="updateData">保存</button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue'
+  import { DB } from '@/config/db'
+
+  const jsonData = ref([])
+  const newData = ref({
+    name: '',
+    logo: '',
+    localLogo: '',
+    sort: 0,
+    description: '',
+    status: 'A' as 'A' | 'D',
+  })
+  const editId = ref<number | null>(null)
+  const editData = ref({
+    name: '',
+    logo: '',
+    localLogo: '',
+    sort: 0,
+    description: '',
+    status: 'A' as 'A' | 'D',
+  })
+  const showModal = ref(false)
+
+  const fetchData = () => {
+    // 查询数据
+    DB.selectFrom('menuCate')
+      .select(['id', 'name', 'logo', 'localLogo', 'sort', 'description', 'status'])
+      .execute()
+      .then((data) => {
+        console.log('data: ', data)
+        jsonData.value = data
+      })
+  }
+
+  const addData = () => {
+    // 添加数据
+    DB.insertInto('menuCate')
+      .values(newData.value)
+      .execute()
+      .then(() => {
+        fetchData()
+        newData.value = {
+          name: '',
+          logo: '',
+          localLogo: '',
+          sort: 0,
+          description: '',
+          status: 'A',
+        }
+      })
+  }
+
+  const openEditModal = (item: any) => {
+    editId.value = item.id
+    editData.value = { ...item }
+    showModal.value = true
+  }
+
+  const closeModal = () => {
+    showModal.value = false
+    editId.value = null
+    editData.value = {
+      name: '',
+      logo: '',
+      localLogo: '',
+      sort: 0,
+      description: '',
+      status: 'A',
+    }
+  }
+
+  const updateData = () => {
+    // 更新数据
+    DB.updateTable('menuCate')
+      .set(editData.value)
+      .where('id', '=', editId.value)
+      .execute()
+      .then(() => {
+        fetchData()
+        closeModal()
+      })
+  }
+
+  const deleteData = (id: number) => {
+    // 删除数据
+    DB.deleteFrom('menuCate')
+      .where('id', '=', id)
+      .execute()
+      .then(() => {
+        fetchData()
+      })
+  }
+
+  fetchData()
+</script>
+
+<style scoped lang="scss">
+  .container {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    height: 100vh;
+    background-color: #f0f0f0;
+    padding: 20px;
+
+    .form-container {
+      margin-top: 20px;
+      display: flex;
+      gap: 10px;
+      flex-wrap: wrap;
+      justify-content: center;
+
+      input,
+      select {
+        padding: 10px;
+        font-size: 16px;
+        border: 1px solid #ccc;
+        border-radius: 5px;
+        width: 200px;
+      }
+
+      button {
+        padding: 10px 20px;
+        font-size: 16px;
+        color: #fff;
+        background-color: #28a745;
+        border: none;
+        border-radius: 5px;
+        cursor: pointer;
+        transition: background-color 0.3s ease;
+
+        &:hover {
+          background-color: #218838;
+        }
+      }
+    }
+
+    .json-container {
+      margin-top: 20px;
+      padding: 20px;
+      background-color: #fff;
+      border: 1px solid #ccc;
+      border-radius: 5px;
+      width: 100%;
+      max-width: 800px;
+      overflow-x: auto;
+
+      table {
+        width: 100%;
+        border-collapse: collapse;
+
+        th,
+        td {
+          padding: 10px;
+          border: 1px solid #ccc;
+          text-align: left;
+        }
+
+        th {
+          background-color: #f8f8f8;
+        }
+
+        button {
+          padding: 5px 10px;
+          font-size: 14px;
+          color: #fff;
+          border: none;
+          border-radius: 5px;
+          cursor: pointer;
+          transition: background-color 0.3s ease;
+          margin-right: 10px;
+
+          &.edit-button {
+            background-color: #007bff;
+
+            &:hover {
+              background-color: #0056b3;
+            }
+          }
+
+          &.delete-button {
+            background-color: #dc3545;
+
+            &:hover {
+              background-color: #c82333;
+            }
+          }
+        }
+      }
+    }
+
+    .modal {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      background-color: rgba(0, 0, 0, 0.5);
+    }
+
+    .modal-content {
+      background-color: #fff;
+      padding: 20px;
+      border-radius: 5px;
+      width: 400px;
+      max-width: 90%;
+      text-align: center;
+
+      .close {
+        position: absolute;
+        top: 10px;
+        right: 10px;
+        font-size: 24px;
+        cursor: pointer;
+      }
+
+      input,
+      select {
+        margin-bottom: 10px;
+        padding: 10px;
+        font-size: 16px;
+        border: 1px solid #ccc;
+        border-radius: 5px;
+        width: 100%;
+      }
+
+      button {
+        padding: 10px 20px;
+        font-size: 16px;
+        color: #fff;
+        background-color: #007bff;
+        border: none;
+        border-radius: 5px;
+        cursor: pointer;
+        transition: background-color 0.3s ease;
+
+        &:hover {
+          background-color: #0056b3;
+        }
+      }
+    }
+  }
+</style>

+ 9 - 0
src/vite-env.d.ts

@@ -0,0 +1,9 @@
+/// <reference types="vite/client" />
+
+interface ImportMetaEnv {
+  readonly VITE_BASE_URL?: string
+}
+
+interface ImportMeta {
+  readonly env: ImportMetaEnv
+}

Some files were not shown because too many files changed in this diff