Ver código fonte

✨ feat(db): 添加数据库操作功能

陈雪 1 semana atrás
pai
commit
1ed9a6e4c5
6 arquivos alterados com 196 adições e 10 exclusões
  1. 1 0
      package.json
  2. 9 0
      pnpm-lock.yaml
  3. 20 5
      src/config/db.ts
  4. 155 0
      src/config/kysely.ts
  5. 10 5
      src/views/layout/components/Header.vue
  6. 1 0
      tsconfig.json

+ 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
 

+ 20 - 5
src/config/db.ts

@@ -1,7 +1,22 @@
-import Database from '@tauri-apps/plugin-sql'
+import TauriDatabase from '@tauri-apps/plugin-sql'
+import { Generated, Insertable, Kysely, Selectable, Updateable } from 'kysely'
+import { D1Dialect } from './kysely'
 
-let db: Database
-export async function initDB() {
-  if (!db) db = await Database.load('sqlite:store.db')
-  return db
+interface Database {
+  todos: TodosTable
 }
+export interface TodosTable {
+  id: Generated<number>
+  title: string
+  status: 1 | 2 | 3 | 4
+}
+
+export type Todo = Selectable<TodosTable>
+export type NewTodo = Insertable<TodosTable>
+export type TodoUpdate = Updateable<TodosTable>
+
+export const DB = new Kysely<Database>({
+  dialect: new D1Dialect({
+    database: await TauriDatabase.load('sqlite:store.db'),
+  }),
+})

+ 155 - 0
src/config/kysely.ts

@@ -0,0 +1,155 @@
+import {
+  CompiledQuery,
+  DatabaseConnection,
+  DatabaseIntrospector,
+  Dialect,
+  Driver,
+  Kysely,
+  SqliteAdapter,
+  SqliteIntrospector,
+  SqliteQueryCompiler,
+  QueryCompiler,
+  QueryResult,
+} from 'kysely'
+import Database from '@tauri-apps/plugin-sql'
+
+/**
+ * Config for the D1 dialect. Pass your D1 instance to this object that you bound in `wrangler.toml`.
+ */
+export interface D1DialectConfig {
+  database: Database
+}
+
+/**
+ * D1 dialect that adds support for [Cloudflare D1][0] in [Kysely][1].
+ * The constructor takes the instance of your D1 database that you bound in `wrangler.toml`.
+ *
+ * ```typescript
+ * new D1Dialect({
+ *   database: env.DB,
+ * })
+ * ```
+ *
+ * [0]: https://blog.cloudflare.com/introducing-d1/
+ * [1]: https://github.com/koskimas/kysely
+ */
+export class D1Dialect implements Dialect {
+  #config: D1DialectConfig
+
+  constructor(config: D1DialectConfig) {
+    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: D1DialectConfig
+
+  constructor(config: D1DialectConfig) {
+    this.#config = config
+  }
+
+  async init(): Promise<void> {}
+
+  async acquireConnection(): Promise<DatabaseConnection> {
+    return new D1Connection(this.#config)
+  }
+
+  async beginTransaction(conn: D1Connection): Promise<void> {
+    return await conn.beginTransaction()
+  }
+
+  async commitTransaction(conn: D1Connection): Promise<void> {
+    return await conn.commitTransaction()
+  }
+
+  async rollbackTransaction(conn: D1Connection): Promise<void> {
+    return await conn.rollbackTransaction()
+  }
+
+  async releaseConnection(_conn: D1Connection): Promise<void> {}
+
+  async destroy(): Promise<void> {}
+}
+
+class D1Connection implements DatabaseConnection {
+  #config: D1DialectConfig
+  //   #transactionClient?: D1Connection
+
+  constructor(config: D1DialectConfig) {
+    this.#config = config
+  }
+
+  async executeQuery<O>(compiledQuery: CompiledQuery): Promise<QueryResult<O>> {
+    console.log('compiledQuery: ', compiledQuery)
+    // Transactions are not supported yet.
+    // if (this.#transactionClient) return this.#transactionClient.executeQuery(compiledQuery)
+
+    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,
+      // @ts-ignore deprecated in kysely >= 0.23, keep for backward compatibility.
+      numUpdatedOrDeletedRows: numAffectedRows,
+    }
+  }
+
+  async beginTransaction() {
+    // this.#transactionClient = this.#transactionClient ?? new PlanetScaleConnection(this.#config)
+    // this.#transactionClient.#conn.execute('BEGIN')
+    throw new Error('Transactions are not supported yet.')
+  }
+
+  async commitTransaction() {
+    // if (!this.#transactionClient) throw new Error('No transaction to commit')
+    // this.#transactionClient.#conn.execute('COMMIT')
+    // this.#transactionClient = undefined
+    throw new Error('Transactions are not supported yet.')
+  }
+
+  async rollbackTransaction() {
+    // if (!this.#transactionClient) throw new Error('No transaction to rollback')
+    // this.#transactionClient.#conn.execute('ROLLBACK')
+    // this.#transactionClient = undefined
+    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')
+  }
+}

+ 10 - 5
src/views/layout/components/Header.vue

@@ -38,11 +38,11 @@
     </div>
   </header>
 </template>
-<script setup>
+<script lang="ts" setup>
 import { computed, ref } from 'vue'
 import { Search } from '@element-plus/icons-vue'
 import { messages } from '../../../i18n/index'
-import { initDB } from '@/config/db'
+import { DB } from '@/config/db'
 import { useI18n } from 'vue-i18n'
 const { t, locale } = useI18n()
 const langs = computed(() => Object.keys(messages))
@@ -52,8 +52,13 @@ function changeLanguage(lang) {
   locale.value = lang
 }
 async function execSQLTest() {
-  const db = await initDB()
-  const reslut = await db.select('SELECT * from todos')
-  console.log('reslut: ', reslut)
+  DB.insertInto('todos')
+    .values({
+      title: '标题1',
+      status: 2,
+    })
+    .executeTakeFirst()
+  const result = await DB.selectFrom('todos').selectAll().where('id', '=', 1).execute()
+  console.log('resulr: ', result)
 }
 </script>

+ 1 - 0
tsconfig.json

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