使

使用 Airtable、Vue + Vuetify 构建一个简单的 CRUD 应用。到目前为止,所有内容都是标准的 Vue、Vuetify 和 Axios 样板。现在,让我们深入了解 CRUD 应用的核心……

2025-06-08

使用 Airtable、Vue + Vuetify 构建一个简单的 CRUD 应用程序

到目前为止,所有内容都是标准的 Vue、Veutify 和 Axios 样板。现在让我们深入了解 CRUD 应用程序的核心……

Airtable + Vue


什么是 Airtable

如果您不熟悉Airtable ,它就像 Google Sheets 与关系型数据库的结合体。最棒的是,它是免费的,拥有简单易用的 GUI(即使没有编程经验的人也能轻松上手)以及数据 API!

创建数据库非常简单,只需在 Airtable 中添加一个“工作表”即可。例如,这里有一个名为“Somedata”的 Airtable,其中包含一个名为“Example”的表……

Airtable

使用 Airtable 提供的各种数据类型添加您需要的任何列/字段。

每个 Airtable 都有一个 API😎

添加工作表和几列后,点击?Airtable 用户界面右上角的(帮助)图标,然后选择“API 文档”。您将看到 Airtable API 的文档……

Airtable API

正如您对简单的 CRUD API 所期望的那样,它提供了ListReadCreateUpdateDelete等 API 方法。每个 API 端点都是一个 URL,由 Airtable ID(又称“应用程序 ID”)和表名组成。API 请求还需要一个唯一的 API 密钥,您也可以在 API 文档中找到该密钥。

例如,这是我的“Somedata”(应用程序 ID:appsAka4wMdRoGCUc)表名为“Example”的 API URL:

API 端点的示例 URL

https://api.airtable.com/v0/appsAka4wMdRoGCUc/Example

使用 Vuetify 的 Vue 应用

使用Vuetify 强大的组件框架,我们来创建 Vue 应用。我添加了一些常量来保存我的 Airtable 设置(稍后会用到它们):

<script>
const apiToken = "keyZoaskXXXX" // use your own key!
const airTableApp = "appXJzFFs2zgj4X5C"
const airTableName = "Example"

new Vue({
  el: '#app',
  vuetify: new Vuetify(),   
})
</script>

<template>
<div id="app">
    <v-app>
        <v-content>
            <h2>
                Vuetify CRUD Example
            </h2>
        </v-content>
    </v-app>
</div>
</template>

Axios 和 Vuetify 数据表

我们将使用Vue Axios与 Airtable API 进行通信。Vuetify 的DataTable 组件用于显示数据,并提供内置的分页排序搜索功能

数据表

我们使用双重用途的对话框组件创建编辑记录。

对话


到目前为止,所有内容都是标准的 Vue、Veutify 和 Axios 样板。现在让我们深入了解 CRUD 应用程序的核心……


1.v-data-table首先让我们在模板标记中设置:

<div id="app">
    <v-app>
        <v-content class="container align-center px-1">
            <h2 class="font-weight-light mb-2">
                Vuetify CRUD Example
            </h2>
            <v-card>
                <v-data-table 
                :headers="headers" 
                :items="items">
                </v-data-table>
            </v-card>
        </v-content>
    </v-app>
</div>

2.添加 Vuedata()itemsheadersVuetify DataTable 使用。注意它们headers与 Airtable 示例列相对应。

new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  data () {
    return {
        headers: [
            { text: 'Id', value: 'id' },
            { text: 'Name', value: 'Name' },
            { text: 'Details', value: 'details', sortable: false, width:"100" },
            { text: 'URL', value: 'url', name:'url', width:"180" },
            { text: 'Action', value: 'actions', sortable: false },
        ],
        items: [],
        dialog: false, // used to toggle the dialog
        editedItem: {} // empty holder for create/update ops
    }
  },
})

从 Airtable API 读取

3.接下来,我们将使用 Axios向 Airtable API发出 GET请求loadItems()(获取)。以下是读取“Example”中所有记录(行)的方法。当 Vue 组件有以下情况时,我们会调用此方法mounted()……

  ...
  mounted() {
    this.loadItems()
  },
  methods: {
    loadItems() {
        this.items = []
        axios.get(`https://api.airtable.com/v0/${airTableApp}/${airTableName}`,
            { headers: { Authorization: "Bearer " + apiToken }})
        .then((response) => {
            // load the API response into items for datatable
            this.items = response.data.records.map((item)=>{
                return {
                    id: item.id,
                    ...item.fields
                }
            })
        }).catch((error) => {
            console.log(error)
        })
    },

请注意,我们现在已经使用了之前创建的 Airtable const。

处理来自 Airtable API 的响应

API响应数据如下所示...

列出来自 Airtable API 的记录响应

{
    "records": [
        {
            "id": "recqbv38i4CbirwP4",
            "fields": {
                "Cat": "bar",
                "URL": "https://codeply.com",
                "Lat": -71.39241,
                "Details": "Frontend editor online",
                "Name": "Codeply",
                "Lon": 41.46,
                "Client": "John Doe"
            },
            "createdTime": "2020-03-25T18:33:52.000Z"
        },
        {
            "id": "recsEjAfLlkKH5rcC",
            "fields": {
                "Cat": "lodging",
                "URL": "https://themestr.app",
                "Lat": -71.39,
                "Details": "Theme builder and customizer for Bootstrap 4",
                "Name": "Themestr.app",
                "Lon": 41.67,
                "Client": "Mary Janes"
            },
            "createdTime": "2020-03-25T18:33:52.000Z"
        },
        ... more rows...
    ]
}

需要注意的是,上面表格数据包含在records数组中,而行数据嵌套在fields属性中。因此,该方法会像这样loadItems()赋值给数据:this.items

         this.items = response.data.records.map((item)=>{
             return {
                 id: item.id,
                 ...item.fields
             }
         })

现在this.items数组具有扁平的结构,易于v-data-table迭代:

   [
        {
            "id": "recqbv38i4CbirwP4",
            "Cat": "bar",
            "URL": "https://codeply.com",
            "Lat": -71.39241,
            "Details": "Frontend editor online",
            "Name": "Codeply",
            "Lon": 41.46,
            "Client": "John Doe".
            "createdTime": "2020-03-25T18:33:52.000Z"
        },
        ... more rows...
   ]

向 Airtable API 发送数据(POST 和 PUT)

4.继续使用saveItem(创建或更新)的方法和deleteItem

  ,
  methods: {
    loadItems() {
        ...
    },
    saveItem(item) {
        /* this is used for both creating and updating API records
         the default method is POST for creating a new item */

        let method = "post"
        let url = `https://api.airtable.com/v0/${airTableApp}/${airTableName}`
        let id = item.id

        // airtable API needs the data to be placed in fields object
        let data = {
            fields: item
        }

        if (id) {
            // if the item has an id, we're updating an existing item
            method = "patch"
            url = `https://api.airtable.com/v0/${airTableApp}/${airTableName}/${id}`

            // must remove id from the data for airtable patch to work
            delete data.fields.id
        }

        // save the record
        axios[method](url,
            data,
            { headers: { 
                Authorization: "Bearer " + apiToken,
                "Content-Type": "application/json"
            }
        }).then((response) => {
            if (response.data && response.data.id) {
                // add new item to state
                this.editedItem.id = response.data.id
                if (!id) {
                    // add the new item to items state
                    this.items.push(this.editedItem)
                }
                this.editedItem = {}
            }
            this.dialog = !this.dialog
        })
    },
    deleteItem(item) {
        let id = item.id
        let idx = this.items.findIndex(item => item.id===id)
        if (confirm('Are you sure you want to delete this?')) {
            axios.delete(`https://api.airtable.com/v0/${airTableApp}/${airTableName}/${id}`,
                { headers: { 
                    Authorization: "Bearer " + apiToken,
                    "Content-Type": "application/json"
                }
            }).then((response) => {
                this.items.splice(idx, 1)
            })
        }
    },

5.现在我们将连接包含用于编辑数据的表单输入框的对话框。它还包含一个用于创建记录的“新建”按钮。在...v-dialog下方添加标记。v-data-table

       <v-card>
            <v-data-table 
             :headers="headers" 
             :items="items" 
             >
             </v-data-table>
             <!-- this dialog is used for both create and update -->
             <v-dialog v-model="dialog">
                  <template v-slot:activator="{ on }">
                    <div class="d-flex">
                        <v-btn color="primary" dark v-on="on">
                            New 
                        </v-btn>
                    </div>
                  </template>
                  <v-card>
                    <v-card-title>
                        <span v-if="editedItem.id">Edit {{editedItem.id}}</span>
                        <span v-else>Create</span>
                    </v-card-title>
                    <v-card-text>
                        <v-row>
                          <v-col cols="12" sm="4">
                            <v-text-field v-model="editedItem.Name" label="Name"></v-text-field>
                          </v-col>
                          <v-col cols="12" sm="8">
                            <v-text-field v-model="editedItem.Details" label="Details"></v-text-field>
                          </v-col>
                          <v-col cols="12" sm="12">
                            <v-text-field v-model="editedItem.URL" label="URL"></v-text-field>
                          </v-col>
                        </v-row>
                    </v-card-text>
                    <v-card-actions>
                      <v-spacer></v-spacer>
                      <v-btn color="blue" text @click="showEditDialog()">Cancel</v-btn>
                      <v-btn color="blue" text @click="saveItem(editedItem)">Save</v-btn>
                    </v-card-actions>
                  </v-card>
            </v-dialog>
       </v-card>

6.然后,添加一个方法来切换对话框的显示:

 methods: {
    showEditDialog(item) {
        this.editedItem = item||{}
        this.dialog = !this.dialog
    },
    loadItems() {...},

7.接下来,自定义插槽模板,使其与编辑现有记录的方法v-data-table item.actions挂钩。同时添加一个图标/按钮showEditDialog()deleteItem()

 <v-data-table 
    :headers="headers" 
    :items="items">
        <template v-slot:item.actions="{ item }">
            <div class="text-truncate">
              <v-icon
                class="mr-2"
                @click="showEditDialog(item)"
                color="primary" 
              >
                mdi-pencil
              </v-icon>
              <v-icon
                @click="deleteItem(item)"
                color="pink" 
              >
                mdi-delete
              </v-icon>
          </div>
        </template>
    </v-data-table>


成品😏是可用的 Vuetify + Airtable CRUD 示例

请在评论中告诉我您对此的想法,并在此处获取完整的源代码https://codeply.com/p/Vx4dDt5c9G/vuetify-crud-example

鏂囩珷鏉ユ簮锛�https://dev.to/codeply/build-a-simple-crud-app-with-airtable-api-vue-vuetify-5565
PREV
GitHub Copilot for CLI 使终端脚本和 Git 变得像提问一样简单
NEXT
使用 Zod 和 TypeScript:前端开发人员指南