24 Angajamente 73391198e5 ... e49077f526

Autor SHA1 Permisiunea de a trimite mesaje. Dacă este dezactivată, utilizatorul nu va putea trimite nici un fel de mesaj Data
  PIWALIN e49077f526 fix:弹窗功能的bug 修改 1 săptămână în urmă
  PIWALIN 71ff87e328 Merge branch 'master' into wl_dev 1 săptămână în urmă
  陈雪 346ecab114 Merge branch 'wl_dev' 1 săptămână în urmă
  陈雪 b2ec0ef752 ✨ feat(test): 将local_logo字段更新为驼峰命名法 1 săptămână în urmă
  陈雪 be65fd3d02 ✨ feat(test): 将本地Logo字段更新为驼峰命名法 1 săptămână în urmă
  陈雪 8407c63126 ✨ feat(database): 将数据库表字段更新为驼峰命名法 1 săptămână în urmă
  陈雪 1f741c25a5 ✨ feat(database): 更新数据库模型字段为驼峰命名法并添加Kysely插件 1 săptămână în urmă
  陈雪 fa8c6b4dc6 ✨ feat(test): 添加描述字段到数据输入和显示 1 săptămână în urmă
  陈雪 23a3217424 ✨ feat(database): 创建菜单分类、商品、SKU和规格表 1 săptămână în urmă
  陈雪 6a56d6460f ✨ feat(database): 添加菜单相关数据表和更新测试页面 1 săptămână în urmă
  陈雪 62a986e4c2 ✨ feat(database): 添加初始数据库迁移脚本和表结构定义 1 săptămână în urmă
  陈雪 4684cea1c5 ✨ feat(test): 重构测试页面,添加数据增删改功能和模态框支持 1 săptămână în urmă
  陈雪 09476155be 🦄 refactor(App): 移除未使用的脚本和样式,优化组件结构 1 săptămână în urmă
  陈雪 b4aa59a68b ✨ feat(test): 添加测试页面和按钮功能,更新路由历史模式 1 săptămână în urmă
  陈雪 3b023bb3c5 🦄 refactor(menu): 清理无用导入和注释,优化代码结构 1 săptămână în urmă
  陈雪 b147519b02 🌈 style(indent): 修改缩进类型 1 săptămână în urmă
  陈雪 99bf64638b 🐞 fix(page): 修复页面样式丢失 1 săptămână în urmă
  陈雪 958828a7d9 🦄 refactor(router): 添加路由类型 1 săptămână în urmă
  陈雪 6597a28401 🎈 perf(db): 优化模块展示功能 1 săptămână în urmă
  陈雪 1ed9a6e4c5 ✨ feat(db): 添加数据库操作功能 1 săptămână în urmă
  陈雪 c4b4e002ef ✨ feat(sql): 添加数据库测试 1 săptămână în urmă
  陈雪 f1f8b61082 ✨ feat(sql): 配置SQL权限 1 săptămână în urmă
  陈雪 6dfeb7f877 Merge branch 'master' of http://1.94.207.143:3000/chongqing/store-project 1 săptămână în urmă
  陈雪 434aa8a011 Merge branch 'lyz_dev' 1 săptămână în urmă

+ 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>
 

+ 1 - 0
package.json

@@ -30,6 +30,7 @@
   "devDependencies": {
     "@tauri-apps/cli": "^2",
     "@vitejs/plugin-vue": "^5.2.1",
+    "kysely": "^0.27.6",
     "sass-embedded": "^1.85.0",
     "typescript": "^5.7.3",
     "unplugin-element-plus": "^0.9.1",

+ 9 - 0
pnpm-lock.yaml

@@ -48,6 +48,9 @@ importers:
       '@vitejs/plugin-vue':
         specifier: ^5.2.1
         version: 5.2.1(vite@6.1.1(jiti@2.4.2)(lightningcss@1.29.1)(sass-embedded@1.85.0))(vue@3.5.13(typescript@5.7.3))
+      kysely:
+        specifier: ^0.27.6
+        version: 0.27.6
       sass-embedded:
         specifier: ^1.85.0
         version: 1.85.0
@@ -688,6 +691,10 @@ packages:
     resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
     hasBin: true
 
+  kysely@0.27.6:
+    resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==}
+    engines: {node: '>=14.0.0'}
+
   lightningcss-darwin-arm64@1.29.1:
     resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==}
     engines: {node: '>= 12.0.0'}
@@ -1576,6 +1583,8 @@ snapshots:
 
   jiti@2.4.2: {}
 
+  kysely@0.27.6: {}
+
   lightningcss-darwin-arm64@1.29.1:
     optional: true
 

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

@@ -2,12 +2,6 @@
   "$schema": "../gen/schemas/desktop-schema.json",
   "identifier": "default",
   "description": "Capability for the main window",
-  "windows": [
-    "main"
-  ],
-  "permissions": [
-    "core:default",
-    "opener:default",
-    "sql:default"
-  ]
-}
+  "windows": ["main"],
+  "permissions": ["core:default", "opener:default", "sql:default", "sql:allow-execute"]
+}

+ 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)
+    );

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

@@ -1,15 +1,22 @@
-// 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_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_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");
 }

+ 6 - 0
src-tauri/tauri.conf.json

@@ -21,9 +21,15 @@
       "csp": null
     }
   },
+  "plugins": {
+    "sql": {
+      "preload": ["sqlite:store.db"]
+    }
+  },
   "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>

+ 1 - 1
src/assets/img/menu/add.svg

@@ -2,4 +2,4 @@
 <rect x="0.333333" y="0.333333" width="39.3333" height="39.3333" rx="7.66667" fill="#F67F20"/>
 <rect x="0.333333" y="0.333333" width="39.3333" height="39.3333" rx="7.66667" stroke="#E6E6E6" stroke-width="0.666667"/>
 <path d="M20.0003 12.2917V20M20.0003 27.7084V20M20.0003 20H27.7087M20.0003 20H12.292" stroke="white" stroke-width="1.85" stroke-linecap="round" stroke-linejoin="round"/>
-</svg>
+</svg>

+ 1 - 1
src/assets/img/menu/delete.svg

@@ -1,4 +1,4 @@
 <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
 <rect x="0.699148" y="0.699148" width="38.6017" height="38.6017" rx="9.73563" stroke="#E03B1F" stroke-width="1.3983"/>
 <path d="M29.969 15.7202L30.1132 15.722C30.6481 15.7618 31.0581 16.1615 31.0865 16.6427L31.0727 16.911L30.6169 21.9343L30.1388 26.8366C30.0376 27.8152 29.9471 28.6407 29.8689 29.2904C29.5973 31.5549 27.964 32.9552 25.5019 32.9967C21.6653 33.0605 17.9775 33.0598 14.3962 32.9901C12.0053 32.9449 10.3961 31.5295 10.1293 29.2996L9.94489 27.6567L9.62263 24.4701L9.29252 20.9732L8.91507 16.7755C8.86815 16.237 9.30317 15.7653 9.8867 15.722C10.4216 15.6823 10.8956 16.0163 11.007 16.4871L11.0504 16.8718L11.4048 20.807L11.7918 24.886C11.9654 26.6517 12.116 28.0807 12.2364 29.0845C12.3885 30.3552 13.132 31.0092 14.4403 31.0339C17.9935 31.1031 21.6538 31.1038 25.4634 31.0404C26.8513 31.0171 27.6066 30.3695 27.7619 29.0748L27.9455 27.4408C27.9992 26.9372 28.0567 26.3813 28.1174 25.7774L28.5048 21.7658L28.9714 16.6185C29.0146 16.1248 29.4477 15.7464 29.969 15.7202ZM8.01604 13.4642C7.43063 13.4642 6.95605 13.0262 6.95605 12.4859C6.95605 11.9906 7.35483 11.5813 7.87221 11.5165L8.01604 11.5076H12.6339C13.1827 11.5076 13.6631 11.1817 13.8349 10.7121L13.877 10.5671L14.2358 8.92001C14.5519 7.82911 15.5781 7.0524 16.7807 6.9648L17.0081 6.95654H22.9907C24.2132 6.95654 25.2928 7.66908 25.7084 8.77009L25.7784 8.981L26.1217 10.5668C26.2295 11.0636 26.6699 11.4346 27.2025 11.498L27.3649 11.5076H31.983C32.5684 11.5076 33.043 11.9456 33.043 12.4859C33.043 12.9811 32.6442 13.3905 32.1269 13.4552L31.983 13.4642H8.01604ZM22.9907 8.91314H17.0081C16.7107 8.91314 16.4463 9.07379 16.3365 9.27571L16.2993 9.36442L15.9558 10.951C15.9137 11.1446 15.8528 11.3311 15.7751 11.5089L24.2239 11.5091C24.1753 11.3982 24.1334 11.2839 24.0984 11.1666L24.0429 10.9506L23.7149 9.42541C23.6383 9.16078 23.4015 8.96704 23.1158 8.92274L22.9907 8.91314Z" fill="#E03B1F"/>
-</svg>
+</svg>

+ 1 - 1
src/assets/img/menu/reduce.svg

@@ -2,4 +2,4 @@
 <rect x="0.333333" y="0.333333" width="39.3333" height="39.3333" rx="7.66667" fill="#F67F20"/>
 <rect x="0.333333" y="0.333333" width="39.3333" height="39.3333" rx="7.66667" stroke="#E6E6E6" stroke-width="0.666667"/>
 <path d="M27.7087 20H20.0003H12.292" stroke="white" stroke-width="1.85" stroke-linecap="round" stroke-linejoin="round"/>
-</svg>
+</svg>

BIN
src/assets/img/minus_Icon.png


BIN
src/assets/img/plus_Icon.png


+ 7 - 6
src/components/SearchInput.vue

@@ -1,9 +1,10 @@
+<!-- 搜索框 -->
 <template>
   <div class="search-container">
     <input
       type="text"
       class="search-input"
-      placeholder="Spicy seasoned seafood noodles"
+      :placeholder="$t('menu.spicyNoodles')"
       v-model="searchQuery"
     />
     <div class="search-icon" @click="searchBtn">
@@ -16,7 +17,6 @@ import { ref } from 'vue'
 
 const searchBtn=()=>{
   console.log(111);
-  
 }
 const searchQuery = ref('')
 </script>
@@ -27,9 +27,10 @@ const searchQuery = ref('')
   align-items: center;
   width: 660px;
   height: 60px;
-  border: 1px solid #ccc;
+  border: 1px solid #E6E6E6;
   border-radius: 12px 0 0 12px;
   background-color: #fff;
+  margin-bottom: 15px;
   .search-input {
     width: 100%;
     height: 40px;
@@ -53,7 +54,7 @@ const searchQuery = ref('')
     background-position: center;
   }
 }
-.search-input::placeholder {
-  color: #999;
-}
+// .search-input::placeholder {
+//   color: #999;
+// }
 </style>

+ 5 - 5
src/components/noProduct.vue

@@ -1,10 +1,10 @@
 <template>
-    <div class="w-full h-[80%] flex flex-col items-center justify-center">
-        <img class="w-[45%]" src="/imgs/noFoods.svg" alt="" />
-        <div class="text-[15px] font-semibold">{{ t('menu.noProducts') }}</div>
-    </div>
+  <div class="w-full h-[80%] flex flex-col items-center justify-center">
+    <img class="w-[45%]" src="/imgs/noFoods.svg" alt="" />
+    <div class="text-[15px] font-semibold">{{ t('menu.noProducts') }}</div>
+  </div>
 </template>
 <script setup>
 import { useI18n } from 'vue-i18n'
 const { t } = useI18n()
-</script>
+</script>

+ 1 - 3
src/components/titleFood.vue

@@ -1,10 +1,9 @@
-
+<!-- 标题 -->
 <template>
     <div :style="{ fontSize: fontSize,}" class="title">
       <slot></slot>
     </div>
   </template>
-  
   <script>
   export default {
     name: 'Title',
@@ -16,7 +15,6 @@
     }
   }
   </script>
-  
   <style scoped>
  .title{
     height: 40px;

+ 10 - 5
src/config/db.ts

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

+ 126 - 0
src/config/kysely.ts

@@ -0,0 +1,126 @@
+import {
+  CompiledQuery,
+  DatabaseConnection,
+  DatabaseIntrospector,
+  Dialect,
+  Driver,
+  Kysely,
+  SqliteAdapter,
+  SqliteIntrospector,
+  SqliteQueryCompiler,
+  QueryCompiler,
+  QueryResult,
+} from 'kysely'
+import Database from '@tauri-apps/plugin-sql'
+
+export interface TauriDialectConfig {
+  database: Database
+}
+
+export class TauriDialect implements Dialect {
+  #config: TauriDialectConfig
+
+  constructor(config: TauriDialectConfig) {
+    this.#config = config
+  }
+
+  createAdapter() {
+    return new SqliteAdapter()
+  }
+
+  createDriver(): Driver {
+    return new D1Driver(this.#config)
+  }
+
+  createQueryCompiler(): QueryCompiler {
+    return new SqliteQueryCompiler()
+  }
+
+  createIntrospector(db: Kysely<any>): DatabaseIntrospector {
+    return new SqliteIntrospector(db)
+  }
+}
+
+class D1Driver implements Driver {
+  #config: TauriDialectConfig
+
+  constructor(config: TauriDialectConfig) {
+    this.#config = config
+  }
+
+  async init(): Promise<void> {}
+
+  async acquireConnection(): Promise<DatabaseConnection> {
+    return new TauriConnection(this.#config)
+  }
+
+  async beginTransaction(conn: TauriConnection): Promise<void> {
+    return await conn.beginTransaction()
+  }
+
+  async commitTransaction(conn: TauriConnection): Promise<void> {
+    return await conn.commitTransaction()
+  }
+
+  async rollbackTransaction(conn: TauriConnection): Promise<void> {
+    return await conn.rollbackTransaction()
+  }
+
+  async releaseConnection(_conn: TauriConnection): Promise<void> {}
+
+  async destroy(): Promise<void> {}
+}
+
+class TauriConnection implements DatabaseConnection {
+  #config: TauriDialectConfig
+  //   #transactionClient?: D1Connection
+
+  constructor(config: TauriDialectConfig) {
+    this.#config = config
+  }
+
+  async executeQuery<O>(compiledQuery: CompiledQuery): Promise<QueryResult<O>> {
+    if (compiledQuery.sql.startsWith('select')) {
+      const results = await this.#config.database.select<O[]>(compiledQuery.sql, [
+        ...compiledQuery.parameters,
+      ])
+      return {
+        rows: results,
+      }
+    }
+    const results = await this.#config.database.execute(compiledQuery.sql, [
+      ...compiledQuery.parameters,
+    ])
+
+    const numAffectedRows = results.rowsAffected > 0 ? BigInt(results.rowsAffected) : undefined
+
+    return {
+      insertId:
+        results.lastInsertId === undefined || results.lastInsertId === null
+          ? undefined
+          : BigInt(results.lastInsertId),
+      rows: [],
+      numAffectedRows,
+      numUpdatedOrDeletedRows: numAffectedRows,
+    }
+  }
+
+  async beginTransaction() {
+    throw new Error('Transactions are not supported yet.')
+  }
+
+  async commitTransaction() {
+    throw new Error('Transactions are not supported yet.')
+  }
+
+  async rollbackTransaction() {
+    throw new Error('Transactions are not supported yet.')
+  }
+
+  async *streamQuery<O>(
+    _compiledQuery: CompiledQuery,
+    _chunkSize: number,
+  ): AsyncIterableIterator<QueryResult<O>> {
+    throw new Error('D1 Driver does not support streaming')
+  }
+}

+ 6 - 1
src/i18n/en.json

@@ -5,5 +5,10 @@
     "sidebar.exit":"Exit",
     
     "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"
 }

+ 6 - 1
src/i18n/zh.json

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

+ 0 - 2
src/main.ts

@@ -7,10 +7,8 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import { createPinia } from 'pinia'
 import i18n from './i18n'
 import './styles.css'
-import { initDB } from './config/db'
 import './element.scss'
 
-initDB()
 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)) {

+ 11 - 0
src/model/index.ts

@@ -0,0 +1,11 @@
+import { MenuCateTable } from './menu_cate'
+import { MenuCommodityTable } from './menu_commodity'
+import { MenuSkuTable } from './menu_sku'
+import { MenuSpecTable } from './menu_spec'
+
+export interface Database {
+  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 - 28
src/router/index.js

@@ -1,28 +0,0 @@
-import { createWebHashHistory, createRouter } from 'vue-router'
-
-import LayoutView from '../views/layout/index.vue'
-import LoginView from '../views/login/index.vue'
-import MenuView from '../views/menu/index.vue'
-import OrderView from '../views/order/index.vue'
-import ExitView from '../views/exit/index.vue'
-
-const routes = [
-    { path: '/login', component: LoginView },
-    {
-        path: '/',
-        component: LayoutView,
-        redirect: '/menu',
-        children: [
-            { path: '/menu', component: MenuView },
-            { path: '/order', component: OrderView },
-            { path: '/exit', component: ExitView },
-        ]
-    },
-    
-]
-
-const router = createRouter({
-    history: createWebHashHistory(),
-    routes,
-})
-export default router

+ 33 - 0
src/router/index.ts

@@ -0,0 +1,33 @@
+import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
+
+import LayoutView from '../views/layout/index.vue'
+import LoginView from '../views/login/index.vue'
+import MenuView from '../views/menu/index.vue'
+import OrderView from '../views/order/index.vue'
+import ExitView from '../views/exit/index.vue'
+
+const routes: RouteRecordRaw[] = [
+  { path: '/login', component: LoginView },
+  {
+    path: '/',
+    component: LayoutView,
+    redirect: '/menu',
+    children: [
+      { path: '/menu', component: MenuView },
+      { path: '/order', component: OrderView },
+      { path: '/exit', component: ExitView },
+    ],
+  },
+  {
+    path: '/test',
+    name: 'Test',
+    component: () => import('@/views/test/index.vue'),
+  },
+]
+
+const router = createRouter({
+  history: createWebHistory(),
+  routes,
+})
+
+export default router

+ 55 - 49
src/views/layout/components/Header.vue

@@ -1,52 +1,58 @@
 <template>
-    <header class="flex h-[60px] items-center px-[48px] border-b-[1px] border-b-[#e6e6e6] justify-between ">
-            <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">
-                <el-dropdown placement="bottom-start" trigger="click" size="large">
-
-                    <div class="flex items-center h-[42px] border rounded-[8px] border-[#e6e6e6] px-[10px]">
-                        <img class="w-[24px] h-[24px] mr-[5px]" src="/imgs/earth.svg" alt="" />
-                          {{ curLang }} 
-                    </div>
-                    <template #dropdown>
-                        <el-dropdown-menu>
-                            <el-dropdown-item v-for="(item,index) in langs" :key="index">
-                                <span @click="locale=item">{{ item }}</span>
-                            </el-dropdown-item>
-                           
-                        </el-dropdown-menu>
-                    </template>
-                </el-dropdown>
-                <div class="flex items-center ml-[10px]">
-                    <div class="w-[42px] h-[42px] overflow-hidden rounded-full">
-                        <img class="w-full h-full" src="/imgs/avater.svg" alt="">
-                    </div>
-                    <div class="ml-[5px] flex flex-col justify-around h-[42px]">
-                        <div class="text-[#333] text-[14px] font-bold">周星星</div>
-                        <div class="text-[#999] text-[10px]">出纳员</div>
-                    </div>
-                </div>
-            </div>
-            <OrderDrawer v-model:show="show" :type="SearchType" />
-        </header>
+  <header
+    class="flex h-[60px] items-center px-[48px] border-b-[1px] border-b-[#e6e6e6] justify-between"
+  >
+    <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">
+      <el-dropdown placement="bottom-start" trigger="click" size="large">
+        <div class="flex items-center h-[42px] border rounded-[8px] border-[#e6e6e6] px-[10px]">
+          <img class="w-[24px] h-[24px] mr-[5px]" src="/imgs/earth.svg" alt="" />
+          {{ curLang }}
+        </div>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item v-for="(item, index) in langs" :key="index">
+              <span @click="locale = item">{{ item }}</span>
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      <div class="flex items-center ml-[10px]">
+        <div class="w-[42px] h-[42px] overflow-hidden rounded-full">
+          <img class="w-full h-full" src="/imgs/avater.svg" alt="" />
+        </div>
+        <div class="ml-[5px] flex flex-col justify-around h-[42px]">
+          <div class="text-[#333] text-[14px] font-bold">周星星</div>
+          <div class="text-[#999] text-[10px]">出纳员</div>
+        </div>
+      </div>
+    </div>
+    <OrderDrawer v-model:show="show" :type="SearchType" />
+  </header>
 </template>
-<script setup>
-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){
+<script lang="ts" setup>
+  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>
+  }
+</script>

+ 19 - 14
src/views/layout/components/Menu.vue

@@ -1,12 +1,18 @@
 <template>
-    <div class="flex flex-col items-center">
-        <div class="rounded-[20px] flex items-center justify-center  w-[45px] h-[45px]" :class="[props.info.slected?'active':'']">
-            <img :src="props.info?.img" class="w-full h-full" />
-        </div>
-        <div class=" text-center text-[12px] w-[75px]" :class="[props.info.slected?'font-semibold':'']">
-            {{ t(props.info.name) }}
-        </div>
+  <div class="flex flex-col items-center">
+    <div
+      class="rounded-[20px] flex items-center justify-center w-[45px] h-[45px]"
+      :class="[props.info.slected ? 'active' : '']"
+    >
+      <img :src="props.info?.img" class="w-full h-full" />
     </div>
+    <div
+      class="text-center text-[12px] w-[75px]"
+      :class="[props.info.slected ? 'font-semibold' : '']"
+    >
+      {{ t(props.info.name) }}
+    </div>
+  </div>
 </template>
 <script setup>
 import { watch } from 'vue'
@@ -30,10 +36,9 @@ const changeLanguage = (lang) => {
 }
 </script>
 <style scoped>
-    .active{
-        border-radius: 19.296px;
-        background: #F67F20;
-        box-shadow: 0px 12.864px 38.593px 0px rgba(234, 124, 105, 0.32);
-    }
-
-</style>
+.active {
+  border-radius: 19.296px;
+  background: #f67f20;
+  box-shadow: 0px 12.864px 38.593px 0px rgba(234, 124, 105, 0.32);
+}
+</style>

+ 52 - 39
src/views/menu/components/CapsuleBox.vue

@@ -1,50 +1,63 @@
+<!-- 胶囊 -->
 <template>
   <div class="capsule-container">
-    <div class="capsule-box" 
-    v-for="(item, index) in items" 
-    :key="index"  
-    @click="changeColor(index)"
-    :class="{'clicked': clickedIndex === index}">
+    <div
+      class="capsule-box"
+      v-for="(item, index) in items"
+      :key="index"
+      @click="changeColor(index,item)"
+      :class="{ clicked: clickedIndex === index }"
+    >
       {{ item.text }}
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref } from 'vue'
-import { defineProps } from 'vue'
-const props = defineProps({
-  items: {
-    type: Array,
-    required: true,
-  },
-})
-// 使用 ref 来追踪哪个盒子被点击
-const clickedIndex = ref(null)
-const changeColor = (index) => {
-  console.log(index,111);
-  clickedIndex.value = index
-}
+  import { ref } from 'vue'
+  import { defineProps } 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>
-.capsule-container {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 16px;
-}
-.capsule-box {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  height: 50px;
-  border-radius: 8px;
-  padding: 24px;
-  color: #000;
-  border: 1px solid #ccc;
-}
-.capsule-box:hover {
-  background-color: rgba(246, 127, 32, 1);
-  color: #fff;
-}
+<style scoped lang="scss">
+  .capsule-container {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 16px;
+  }
+  .capsule-box {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 50px;
+    border-radius: 8px;
+    padding: 24px;
+    color: #000;
+    border: 1px solid #e6e6e6;
+  }
+  .capsule-box:hover {
+    background-color: #f67f20;
+    color: #fff;
+  }
+  .capsule-box.clicked {
+    background-color: #f67f20; /* 当点击时的背景颜色 */
+    color: #fff; /* 选中时的文字颜色 */
+  }
 </style>

+ 83 - 78
src/views/menu/components/menuFood.vue

@@ -1,108 +1,113 @@
+<!-- 菜品 -->
 <template>
   <div class="food_warp">
-    <div class="food_container" @click="food_btn">
+    <div class="food_container" @click="food_btnNumber">
       <div class="item">
         <img :src="imageUrl" alt="Food Image" />
       </div>
-      <div class="w-[300px] h-[80px] flex items-center ">
+      <div class="w-[300px] 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="showNumber">
+    <div class="w-[398px] h-[102px]" v-show="showNumberClose">
       <Title fontSize="16px">Number</Title>
       <div class="food_number">
-        <div @click="handleChange(-1)" class="numItem">-</div>
+        <div @click="handleChange(-1)" >
+          <img src="../../../assets//img//minus_Icon.png" alt="">
+        </div>
         <div class="numItem">{{ num }}</div>
-        <div @click="handleChange(1)" class="numItem">+</div>
+        <div @click="handleChange(1)" >
+          <img src="../../../assets//img//plus_Icon.png" alt="">
+        </div>
       </div>
     </div>
-    <!-- <el-divider /> -->
   </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: {
+  import Title from '@/components/titleFood.vue'
+  import { ref } from 'vue'
+  const props = defineProps({
+    imageUrl: {
       type: String,
       required: true,
     },
-})
-const num = ref(1)
-const showNumber = ref(false)
-const handleChange = (value) => {
-  if (num.value > 0 || value > 0) {
-    num.value += value
-  }
-}
-const food_btn = () => {
-    if (props.type == 'Search') {
-      showNumber.value = true;
+    productName: {
+      type: String,
+      required: true,
+    },
+    price: {
+      type: String,
+      required: true,
+    },
+    quantity: {
+      type: String,
+      required: true,
+    },
+    type: {
+      type: String,
+      required: true,
+    },
+  })
+  const num = ref(1)
+  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%;
-  height: 80px;
-  margin-top: 15px;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  .item {
-    width: 100px;
-    img {
+  .food_warp {
+    display: flex;
+    flex-wrap: wrap;
+    justify-content: space-between;
+    align-items: center;
+  }
+  .food_container {
+    width: 100%;
+    height: 80px;
+    // padding-bottom: 15px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .item {
       width: 100px;
-      height: 80px;
-      border-radius: 50%;
+      img {
+        width: 64px;
+        height: 64px;
+        border-radius: 50%;
+      }
     }
   }
-}
-.food_number {
-  display: flex;
-  height: 60px;
-  align-items: center;
-}
+  .food_number {
+    display: flex;
+    height: 60px;
+    align-items: center;
+  }
 .numItem {
-  width: 40px;
-  height: 40px;
-  background-color: rgba(246, 127, 32, 1);
-  border-radius: 8px;
-  margin-right: 10px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: #fff;
-  font-size: 20px;
-}
-.flex .numItem:nth-child(2) {
-  background-color: transparent;
-  color: #000;
-  border: 1px solid #ccc;
-}
+    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>

+ 176 - 151
src/views/menu/components/order-drawer.vue

@@ -1,165 +1,190 @@
 <template>
   <div>
-  <el-drawer v-model="show" :show-close="false" direction="rtl" size="700px">
-    <template #header>
-      <div class="h-[56px] text-[#000] text-[24px] font-semibold border-b border-[#ccc]">Search for products
-      </div>
-    </template>
-
-    <template #default>
-      <SearchInput></SearchInput>
-      <!-- 判断从那点击进来 Search点击搜索框 -->
-      <div v-if="type === 'Search'" class="food_main">
-        <!-- 判断foodItems 显示图片 -->
-        <div v-if="foodItems.length === 0" class="search_product"></div>
-        <div v-else>
-          <menuFood
-          v-for="(item, index) in foodItems"
-          :key="index"
-          :imageUrl="item.imageUrl"
-          :productName="item.productName"
-          :price="item.price"
-          :quantity="item.quantity"
-          :type="type"
-        />
+    <el-drawer v-model="show" :show-close="false" direction="rtl" size="700px">
+      <template #header>
+        <div class="food_title">
+          {{ $t('menu.search') }}
         </div>
-      </div>
-      <div v-else>
-        <div>
-          <!-- <el-divider /> -->
-          <menuFood
-            v-for="(item, index) in foodItems"
-            :key="index"
-            :imageUrl="item.imageUrl"
-            :productName="item.productName"
-            :price="item.price"
-            :quantity="item.quantity"
-            :type="type" 
-          />
-          <Title fontSize="16px">Taste</Title>
-          <CapsuleBox :items="foodList" />
-          <Title fontSize="16px">Type</Title>
-          <CapsuleBox :items="foodList" />
-          <Title fontSize="16px">Number</Title>
-          <div class="flex h-[60px] items-center ">
-            <div @click="handleChange(-1)" class="numItem">-</div>
-            <div class="numItem">{{num}}</div>
-            <div @click="handleChange(1)" class="numItem">+</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.imageUrl"
+                :productName="item.productName"
+                :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.imageUrl"
+                :productName="item.productName"
+                :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 h-[60px] 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.imageUrl"
+                :productName="item.productName"
+                :price="item.price"
+                :quantity="item.quantity"
+                :type="type"
+              />
+            </div>
           </div>
-          <el-divider />
-          <menuFood
-            v-for="(item, index) in foodItems"
-            :key="index"
-            :imageUrl="item.imageUrl"
-            :productName="item.productName"
-            :price="item.price"
-            :quantity="item.quantity"
-            :type="type" 
-          />
         </div>
-      </div>
-    </template>
+      </template>
 
-    <template #footer>
-      <div v-if="type === 'Search'">
-        <el-button type="primary" class="w-[660px] h-[68px]" @click="cancelClick" plain>
-          close window
-        </el-button>
-      </div>
-      <div style="flex: auto" v-else>
-        <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>
+      <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>
-    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' },
-      { id: 2, text: 'sautéed' },
-      { id: 3, text: 'sautéed' },
-      { id: 4, text: 'sautéed' },
-    ])
-    // 假数据,用于动态渲染
-    const foodItems = ref([
-      {
-        imageUrl: 'https://www.w3schools.com/w3images/fjords.jpg',
-        productName: 'Search for products 1',
-        price: '\$5.29',
-        quantity: 'x5'
-      },
-      // {
-      //   imageUrl: 'https://www.w3schools.com/w3images/lights.jpg',
-      //   productName: 'Search for products 2',
-      //   price: '\$3.99',
-      //   quantity: 'x3'
-      // },
-      // {
-      //   imageUrl: 'https://www.w3schools.com/w3images/mountains.jpg',
-      //   productName: 'Search for products 3',
-      //   price: '\$8.50',
-      //   quantity: 'x2'
-      // },
-      // {
-      //   imageUrl: 'https://www.w3schools.com/w3images/fjords.jpg',
-      //   productName: 'Search for products 1',
-      //   price: '\$5.29',
-      //   quantity: 'x5'
-      // },
-    ])
-    const cancelClick = () => {
-      show.value = false
-    }
-    const handleChange = (value) => {
-      if (num.value > 0 || value > 0) {
-            num.value += value
-          }
+  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([
+    {
+      imageUrl: 'https://www.w3schools.com/w3images/fjords.jpg',
+      productName: 'Search for products 1',
+      price: '\$5.29',
+      quantity: 'x5',
+    },
+  ])
+  const foodItems = ref([
+    {
+      imageUrl: 'https://www.w3schools.com/w3images/fjords.jpg',
+      productName: 'Search for products 1',
+      price: '\$5.29',
+      quantity: 'x5',
+    },
+    {
+      imageUrl: 'https://www.w3schools.com/w3images/lights.jpg',
+      productName: 'Search for products 2',
+      price: '\$3.99',
+      quantity: 'x3'
+    },
+    {
+      imageUrl: 'https://www.w3schools.com/w3images/mountains.jpg',
+      productName: 'Search for products 3',
+      price: '\$8.50',
+      quantity: 'x2'
+    },
+    {
+      imageUrl: 'https://www.w3schools.com/w3images/fjords.jpg',
+      productName: 'Search for products 1',
+      price: '\$5.29',
+      quantity: 'x5'
+    },
+  ])
+  const cancelClick = () => {
+    show.value = false
+  }
+  const handleChange = (value) => {
+    if (num.value > 0 || value > 0) {
+      num.value += value
     }
+  }
 </script>
 
 <style lang="scss" scoped>
- ::v-deep .el-drawer__header {
-  margin-bottom: 0px !important;
-}
-.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;
-  background-color: rgba(246, 127, 32, 1);
-  border-radius: 8px;
-  margin-right: 10px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: #fff;
-  font-size: 20px;
-}
-.flex .numItem:nth-child(2) {
-  background-color: transparent;
-  color: #000;
-  border: 1px solid #ccc;
-}
+  ::v-deep .el-drawer__header {
+    margin-bottom: 0px !important;
+  }
+  // ::v-deep .el-divider {
+  //   margin: 20px 0px;
+  // }
+  ::v-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>

+ 115 - 110
src/views/menu/components/rightOrder/components/order.vue

@@ -1,122 +1,127 @@
 <!-- 代码已包含 CSS:使用 TailwindCSS , 安装 TailwindCSS 后方可看到布局样式效果 -->
 
 <template>
-    <div class="h-full bg-gray-50">
-      <div class="w-full mx-auto ">
-        <div class="bg-white rounded-lg  p-3">
-          <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 v-for="(item, index) in orderItems" :key="index" 
-              class="flex items-center justify-between p-4 bg-white border border-gray-100 rounded-lg">
-              <div class="flex items-center space-x-4">
-                <div class="w-16 h-16 rounded-full overflow-hidden">
-                  <img :src="item.image" :alt="item.name" class="w-full h-full object-cover"/>
-                </div>
-                <div>
-                  <h3 class="font-sm">{{ item.name }}</h3>
-                  <p class="text-xxs text-gray-500">Size: Large, Middle, Small</p>
-                  <p class="text-orange-500 font-medium mt-1">${{ item.price.toFixed(2) }}</p>
-                </div>
-              </div>
-              <div class="flex items-center space-x-4">
-                <button class="w-4 h-4 flex items-center justify-center bg-orange-100 text-orange-500 rounded-lg !rounded-button whitespace-nowrap"
-                  @click="decreaseQuantity(index)">
-                  <img src="@/assets/img/menu/reduce.svg" alt="">
-                  
-                </button>
-                <span class="w-4 text-center">{{ item.quantity }}</span>
-                <button class="w-4 h-4 flex items-center justify-center bg-orange-500 text-white rounded-lg !rounded-button whitespace-nowrap"
-                  @click="increaseQuantity(index)">
-                  <img src="@/assets/img/menu/add.svg" alt="">
+  <div class="h-full bg-gray-50">
+    <div class="w-full mx-auto">
+      <div class="bg-white rounded-lg p-3">
+        <div class="flex items-center justify-between mb-3">
+          <h1 class="text-sm font-medium">Orders #45892</h1>
+        </div>
 
-                </button>
-                <button class="w-4 h-4 flex items-center justify-center text-gray-400 hover:text-red-500 !rounded-button whitespace-nowrap"
-                  @click="removeItem(index)">
-                  <img src="@/assets/img/menu/delete.svg" alt="">
-                </button>
+        <div class="space-y-4 mb-6 overflow-auto h-[200px]">
+          <div
+            v-for="(item, index) in orderItems"
+            :key="index"
+            class="flex items-center justify-between p-4 bg-white border border-gray-100 rounded-lg"
+          >
+            <div class="flex items-center space-x-4">
+              <div class="w-16 h-16 rounded-full overflow-hidden">
+                <img :src="item.image" :alt="item.name" class="w-full h-full object-cover" />
+              </div>
+              <div>
+                <h3 class="font-sm">{{ item.name }}</h3>
+                <p class="text-xxs text-gray-500">Size: Large, Middle, Small</p>
+                <p class="text-orange-500 font-medium mt-1">${{ item.price.toFixed(2) }}</p>
               </div>
             </div>
-          </div>
-  
-          <div class="border-t border-gray-100 pt-4">
-            <div class="mb-4">
-              <h3 class="font-medium mb-2">Order Notes</h3>
-              <p class="text-gray-600 text-sm">No Chili, No Cilantro</p>
-            </div>
-            
-            <div class="flex items-center justify-between pt-4 border-t border-gray-100">
-              <span class="font-medium">Total</span>
-              <span class="text-sm font-medium">${{ total.toFixed(2) }}</span>
+            <div class="flex items-center space-x-4">
+              <button
+                class="w-4 h-4 flex items-center justify-center bg-orange-100 text-orange-500 rounded-lg !rounded-button whitespace-nowrap"
+                @click="decreaseQuantity(index)"
+              >
+                <img src="@/assets/img/menu/reduce.svg" alt="" />
+              </button>
+              <span class="w-4 text-center">{{ item.quantity }}</span>
+              <button
+                class="w-4 h-4 flex items-center justify-center bg-orange-500 text-white rounded-lg !rounded-button whitespace-nowrap"
+                @click="increaseQuantity(index)"
+              >
+                <img src="@/assets/img/menu/add.svg" alt="" />
+              </button>
+              <button
+                class="w-4 h-4 flex items-center justify-center text-gray-400 hover:text-red-500 !rounded-button whitespace-nowrap"
+                @click="removeItem(index)"
+              >
+                <img src="@/assets/img/menu/delete.svg" alt="" />
+              </button>
             </div>
           </div>
         </div>
+
+        <div class="border-t border-gray-100 pt-4">
+          <div class="mb-4">
+            <h3 class="font-medium mb-2">Order Notes</h3>
+            <p class="text-gray-600 text-sm">No Chili, No Cilantro</p>
+          </div>
+
+          <div class="flex items-center justify-between pt-4 border-t border-gray-100">
+            <span class="font-medium">Total</span>
+            <span class="text-sm font-medium">${{ total.toFixed(2) }}</span>
+          </div>
+        </div>
       </div>
     </div>
-  </template>
-  
-  <script lang="ts" setup>
-  import { ref, computed } from 'vue';
-  
-  interface OrderItem {
-    name: string;
-    price: number;
-    quantity: number;
-    image: string;
-  }
-  
-  const orderItems = ref<OrderItem[]>([
-    {
-      name: 'Spicy Seasoned Seafood',
-      price: 4.85,
-      quantity: 2,
-      image: 'https://ai-public.mastergo.com/ai/img_res/1c3d3c5704265a819a659a8d3589bc21.jpg'
-    },
-    {
-      name: 'Premium Grade Mackerel (Cleaned)',
-      price: 4.65,
-      quantity: 2,
-      image: 'https://ai-public.mastergo.com/ai/img_res/6d4be10ce5e234dcec1509d199a41a99.jpg'
-    },
-    {
-      name: 'Grilled Sea Bass',
-      price: 4.85,
-      quantity: 2,
-      image: 'https://ai-public.mastergo.com/ai/img_res/a20aaccbc775fd5729c8b8c6dac46563.jpg'
-    }
-  ]);
-  
-  const total = computed(() => {
-    return orderItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0);
-  });
-  
-  const increaseQuantity = (index: number) => {
-    orderItems.value[index].quantity += 1;
-  };
-  
-  const decreaseQuantity = (index: number) => {
-    if (orderItems.value[index].quantity > 1) {
-      orderItems.value[index].quantity -= 1;
-    }
-  };
-  
-  const removeItem = (index: number) => {
-    orderItems.value.splice(index, 1);
-  };
-  </script>
-  
-  <style scoped>
-  .order-item {
-    transition: all 0.3s ease;
-  }
-  
-  input[type="number"]::-webkit-inner-spin-button,
-  input[type="number"]::-webkit-outer-spin-button {
-    -webkit-appearance: none;
-    margin: 0;
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed } from 'vue'
+
+interface OrderItem {
+  name: string
+  price: number
+  quantity: number
+  image: string
+}
+
+const orderItems = ref<OrderItem[]>([
+  {
+    name: 'Spicy Seasoned Seafood',
+    price: 4.85,
+    quantity: 2,
+    image: 'https://ai-public.mastergo.com/ai/img_res/1c3d3c5704265a819a659a8d3589bc21.jpg',
+  },
+  {
+    name: 'Premium Grade Mackerel (Cleaned)',
+    price: 4.65,
+    quantity: 2,
+    image: 'https://ai-public.mastergo.com/ai/img_res/6d4be10ce5e234dcec1509d199a41a99.jpg',
+  },
+  {
+    name: 'Grilled Sea Bass',
+    price: 4.85,
+    quantity: 2,
+    image: 'https://ai-public.mastergo.com/ai/img_res/a20aaccbc775fd5729c8b8c6dac46563.jpg',
+  },
+])
+
+const total = computed(() => {
+  return orderItems.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
+})
+
+const increaseQuantity = (index: number) => {
+  orderItems.value[index].quantity += 1
+}
+
+const decreaseQuantity = (index: number) => {
+  if (orderItems.value[index].quantity > 1) {
+    orderItems.value[index].quantity -= 1
   }
-  </style>
-  
-  
+}
+
+const removeItem = (index: number) => {
+  orderItems.value.splice(index, 1)
+}
+</script>
+
+<style scoped>
+.order-item {
+  transition: all 0.3s ease;
+}
+
+input[type='number']::-webkit-inner-spin-button,
+input[type='number']::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
+</style>

+ 31 - 29
src/views/menu/components/rightOrder/rightOrder.vue

@@ -1,36 +1,38 @@
 <!-- 代码已包含 CSS:使用 TailwindCSS , 安装 TailwindCSS 后方可看到布局样式效果 -->
 
 <template>
-    <div class=" h-full bg-white flex flex-col items-center justify-between ">
-      <!-- Empty state section -->
-      <div class="h-[80%]">
-        <noProduct v-if="false"></noProduct>
-        <order v-else></order>
-      </div>
-      <!-- Action buttons section -->
-      <div class="w-full max-w-md px-1  h-[20%] bg-white">
-        <div class="grid grid-cols-2 gap-4 mb-1">
-          <button class="rounded text-sm py-3 px-6 bg-white border border-gray-200 text-gray-700 !rounded-button whitespace-nowrap hover:bg-gray-50 transition-colors">
-            Registration
-          </button>
-          <button class="rounded text-sm py-3 px-6 bg-white border border-gray-200 text-gray-700 !rounded-button whitespace-nowrap hover:bg-gray-50 transition-colors">
-            Kitchen Receipt
-          </button>
-        </div>
-        <button class="rounded text-sm w-full py-3 px-6 mb-1 text-white bg-[#999]  !rounded-button whitespace-nowrap hover:bg-gray-200 transition-colors">
-          Choose A Table For Payment
+  <div class="h-full bg-white flex flex-col items-center justify-between">
+    <!-- Empty state section -->
+    <div class="h-[80%]">
+      <noProduct v-if="false"></noProduct>
+      <order v-else></order>
+    </div>
+    <!-- Action buttons section -->
+    <div class="w-full max-w-md px-1 h-[20%] bg-white">
+      <div class="grid grid-cols-2 gap-4 mb-1">
+        <button
+          class="rounded text-sm py-3 px-6 bg-white border border-gray-200 text-gray-700 !rounded-button whitespace-nowrap hover:bg-gray-50 transition-colors"
+        >
+          Registration
+        </button>
+        <button
+          class="rounded text-sm py-3 px-6 bg-white border border-gray-200 text-gray-700 !rounded-button whitespace-nowrap hover:bg-gray-50 transition-colors"
+        >
+          Kitchen Receipt
         </button>
       </div>
+      <button
+        class="rounded text-sm w-full py-3 px-6 mb-1 text-white bg-[#999] !rounded-button whitespace-nowrap hover:bg-gray-200 transition-colors"
+      >
+        Choose A Table For Payment
+      </button>
     </div>
-  </template>
-  
-  <script lang="ts" setup>
-  import noProduct from '@/components/noProduct.vue';
-  import order from './components/order.vue';
-  </script>
-  
-  <style scoped>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import noProduct from '@/components/noProduct.vue'
+import order from './components/order.vue'
+</script>
 
-  </style>
-  
-  
+<style scoped></style>

+ 0 - 110
src/views/menu/components/test.vue

@@ -1,110 +0,0 @@
-<!-- 代码已包含 CSS:使用 TailwindCSS , 安装 TailwindCSS 后方可看到布局样式效果 -->
-
-<template>
-    <div class="min-h-screen bg-gray-50">
-      <div class="container mx-auto px-4 py-8">
-        <header class="mb-8">
-          <h1 class="text-3xl font-bold text-gray-900">Fresh Food Menu</h1>
-          <div class="flex items-center mt-4">
-            <div class="relative flex-1 max-w-lg">
-              <input
-                type="text"
-                placeholder="Search for dishes..."
-                class="w-full pl-10 pr-4 py-2 text-sm text-gray-700 bg-white rounded-lg border-none shadow-sm"
-              />
-              <i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-sm"></i>
-            </div>
-          </div>
-        </header>
-  
-        <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
-          <div v-for="product in products" :key="product.id" class="bg-white rounded-xl shadow-sm p-4 transition-transform hover:scale-105">
-            <div class="relative aspect-square mb-4 rounded-full overflow-hidden">
-              <img :src="product.image" :alt="product.name" class="w-full h-full object-cover" />
-            </div>
-            <div class="text-center">
-              <h3 class="text-gray-800 font-medium text-sm mb-2">{{ product.name }}</h3>
-              <p class="text-orange-500 font-semibold">${{ product.price.toFixed(2) }}</p>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  </template>
-  
-  <script lang="ts" setup>
-  import { ref } from 'vue';
-  
-  interface Product {
-    id: number;
-    name: string;
-    price: number;
-    image: string;
-  }
-  
-  const products = ref<Product[]>([
-    {
-      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'
-    }
-  ]);
-  </script>
-  
-  <style scoped>
-  .container {
-    max-width: 1440px;
-    min-height: 1024px;
-  }
-  
-  input[type="number"]::-webkit-inner-spin-button,
-  input[type="number"]::-webkit-outer-spin-button {
-    -webkit-appearance: none;
-    margin: 0;
-  }
-  </style>
-  
-  

+ 73 - 127
src/views/menu/index.vue

@@ -72,138 +72,84 @@
     <div class="w-[500px] h-full overflow-hidden border-l border-gray-200">
       <rightOrder></rightOrder>
     </div>
-    <OrderDrawer v-model:show="show" :type="product" />
+    <OrderDrawer v-model:show="show" :type="productFood" />
   </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 { ref } from 'vue'
+  import { ArrowRight, ArrowLeft } from '@element-plus/icons-vue'
+  import rightOrder from './components/rightOrder/rightOrder.vue'
+  import OrderDrawer from './components/order-drawer.vue'
 
-const { t } = useI18n()
-const store = useStore()
-const { count } = storeToRefs(store)
-// 响应式数据
-const currentIndex = ref(0)
-const itemWidth = ref(0)
-const scrollRef = ref(null)
-
-// 计算元素宽度
-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)
-const product = ref('product'); 
-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()
-})
-
-// 左按钮点击
-const handleLeftClick = () => {
-  if (currentIndex.value <= 0) return
-  currentIndex.value--
-  scrollToPosition()
-}
-
-// 右按钮点击
-const handleRightClick = () => {
-  // 假设products通过props传入
-  if (currentIndex.value >= products.value.length - 1) return
-  currentIndex.value++
-  scrollToPosition()
-}
-
-// 执行滚动
-const scrollToPosition = () => {
-  scrollRef.value?.scrollTo({
-    left: itemWidth.value * currentIndex.value,
-    behavior: 'smooth',
-  })
-}
+  const show = ref(false)
+  const productFood = ref('productFood')
+  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',
+    },
+  ])
 </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>

+ 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>

+ 1 - 0
tsconfig.json

@@ -1,5 +1,6 @@
 {
   "compilerOptions": {
+    "target": "ESNext",
     "composite": true,
     "skipLibCheck": true,
     "module": "ESNext",