nuxt数据库之增删改查,父组件子组件传值

nuxt学到数据库这里,就涉及到响应数据,父组件向子组件传值,子组件向父组件传值,最终还是需要掌握vue3的组件知识了。学习真的是一个长期的过程,不管学习了什么知识,有多少,都应该及时的记录下来,这个记录的过程,就是不断巩固自身知识的过程。之前学习其他框架的时候,不用,过两天就忘记了,当许多插件解决问题,忘记这个插件名字后,那想不起来的时候就特别痛苦了。好记忆不如烂笔头,及时记录下来吧。

ui.nuxt.com中的form章节,有很多实例代码:Form - Nuxt UI

还有,github上有很多样例代码,比如这个Table的代码: https://github.com/nuxt/ui/blob/dev/docs/components/content/examples/TableExampleAdvanced.vue

这个Table例子看了好多遍,代码还是掌握不了啊,感觉有点难啊。Table - Nuxt UI

还是根据table上面的简单的例子,做一些修改吧,从简单的入手。

首先接着之前的文章,把drizzle orm的增删改查等做成对应的api接口路由。https://hub.nuxt.com/docs/recipes/drizzle#usage

读:select

server/api/users/index.get.ts

TypeScript 复制代码
export default eventHandler(async () => {
  const todos = await useDrizzle().select().from(tables.users).all()

  return todos
})

增:insert

server/api/users/index.post.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { name, email, password, avatar, createdAt } = await readBody(event);
  const todo = await useDrizzle()
    .insert(tables.users)
    .values({
      name,
      email,
      password,
      avatar,
      createdAt: new Date(),
    })
    .returning()
    .get();

  return todo;
});

改:update

server/api/users/[id].patch.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { id } = getRouterParams(event);
  const { completed } = await readBody(event);

  const todo = await useDrizzle()
    .update(tables.users)
    .set({
      completed,
    })
    .where(eq(tables.users.id, Number(id)))
    .returning()
    .get();

  return todo;
});

删:delete

server/api/users/[id].delete.ts

TypeScript 复制代码
export default eventHandler(async (event) => {
  const { id } = getRouterParams(event);

  const deletedTodo = await useDrizzle()
    .delete(tables.users)
    .where(and(eq(tables.users.id, Number(id))))
    .returning()
    .get();

  if (!deletedTodo) {
    throw createError({
      statusCode: 404,
      message: 'User not found',
    });
  }
  return deletedTodo;
});

读取的时候,都是一次性的把数据全部select出来,这个当然是不对的,不过初学的时候,先这样应付着写,之后看看怎么带参数select,进行一页一页的读取。

修改和删除的时候,是根据id来操作的,这个时候,url中就需要带上id的值这个参数了。

使用UTable控件把表渲染出来。第一部就是添加数据了。先进行简单的:

TypeScript 复制代码
    const datas = {
        name: 'John Doe',
        email: 'john@example.com',
        password: 'password123',
        avatar: 'https://example.com/avatar/john.png',
        createdAt: new Date(),
}



$fetch('/api/users', {
      method: 'POST',
      body: datas,
    }).then((users: any) => {
      useToast().add({
        title: `${users.id}号添加成功!`,
        description: `
            用户名:${users.name}
            邮箱:${users.email}
            `,
      });
    });

先定义需要添加的数据,然后使用$fetch进行post添加。成功后弹出消息提示一下。

下一步就是点击按钮,弹窗添加新用户,弹出的窗口做成控件,那就涉及到父组件向子组件传值,和子组件向父组件传值的了。

简单来说,子组件向父组件传值,就是要emit,子组件emit一个方法,在父组件用这个方法名绑定一个新的方法,父组件上的新的方法就可以执行子组件方法里的方法了,也可以执行父组件上的其他方法。

父组件向子组件传值,那就更简单了,就是子组件先定义一个属性值,不用赋初始值,在父组件上使用的时候,给这个属性值绑定一个数值,在子组件里,就可以直接获取这个属性值的值(父组件上绑定的值)。

先看子组件向父组件传值(方法):【Vue/Nuxt.js】使用Composition API实现子组件向父组件传递数据的方法 - TeHub

TypeScript 复制代码
子组件
<script setup>
const emit = defineEmits(['hogeEvent']); //emit之外也可以
const hoge = () =>{
    emit('hogeEvent');
}
</script>
<template>
    <button type="button" @click="hoge">emit测试</button>
</template>
父组件
<script setup>
const customEvent = () =>{
  console.log('test')
}
</script>
<template>
  <ChildComponents @hogeEvent="customEvent"></ChildComponents>
</template>

在我这个user的实例中,有很多属性值,所可以emit可以多个函数值:

TypeScript 复制代码
/**
 * 属性
 */
type Props = { modelValue: boolean; info: number };
// defineProps<Props>();
const props = defineProps<Props>();
const { info } = toRefs(props);

最后一行info被从属性值里解构出来变成响应式数据后,就可以直接使用了。

TypeScript 复制代码
/**
 * 事件
 */
const emit = defineEmits(['parentRefresh', 'update:modelValue', 'hogeEvent']);

const onUpdate = (value: boolean) => {
  emit('update:modelValue', value);
};

const parentRefresh = () => {
  const data = { message: '这是子组件传递的数据' };
  emit('parentRefresh', data);
};

emit可以定义多个方法,并且需要在下面进行方法的实现,如果父组件中在绑定的方法中有其他方法需要执行,比如刷新表refresh()操作,就需要在最后执行一下parentRefresh()

父组件中绑定的方法或属性值:

TypeScript 复制代码
      <DataUserForm
        v-model="isOpen"
        @parentRefresh="showrefresh"
        @hoge-event="customEvent"
        :info="info"
      />


const isOpen = ref(false);

const info = computed(() => {
  console.log(users.value.length);

  return users.value.length;
});

/**
 * 添加用户 按钮事件
 */
const addUser = () => {
  isOpen.value = true;
};
const showrefresh = (data: any) => {
  console.log('父组件接收到的数据:', data);
  refresh();
};
const customEvent = () => {
  console.log('test');
};

isOpen是父组件上的按钮点击后显示子组件弹窗的UModal控件。info在父组件是计算select出来所有users表的数据长度,定义的时候,涉及到其他数据来源,所以需要使用computed方法,才能让数据保持响应式数据,自动同步更新。这个打印数据的动作console.log(users.value.length);,就是测试数据是否同步:

打印出来第一次的0,就是info的初始值0,第二次59,是获取数据库数据后,computed计算出来的值。或者加上服务端客服端执行判断:

TypeScript 复制代码
const info = computed(() => {
  if (import.meta.server) {
    console.log('服务端执行:' + users.value.length);
  }
  if (import.meta.client) {
    console.log('客户端端执行:' + users.value.length);
  }
  return users.value.length;
});

这样看就比较明显了,先执行客户端,info的值为0,然后等从数据库select的值获取到后,再次同步了info的值59,客户端执行了2次。 这个就是响应式数据自动更新的动作,数据变动,就是computed再次执行一次。

refresh()刷新数据表,就是读取数据方法useLazyFetch的一个互动操作:

TypeScript 复制代码
const { status, data, refresh } = await useLazyFetch<any>('/api/users');

const users = computed(() => {
  filtered.value.sort((a: any, b: any) => {
    return b.id - a.id;
  });
  return filtered.value;
});


  <UTable
    :columns="columns"
    :emptyState="table.emptyState"
    :loadingState="table.loadingState"
    :rows="rows"
    :loading="status === 'pending'"
    class="mb-2"
  >

:loading="status === 'pending'" 是加载的时候,在获取数据前有个加载动画。获取的对象数组的排序方法非常有意思:

TypeScript 复制代码
  array.sort((a: any, b: any) => {
    return b.id - a.id;
  });

这个是降序,如果想升序,return a.id-b.id就可以了。

还有一个知识点,就是node时间格式化操作,其他框架有不少操作插件,安装之后就可以进行日期格式化了,nuxt上搜索了一些插件,比如moment,time等,研究一下还是不行,最后点到new Date()上发现,typescript官方就有日期本地格式化的操作啊!!!

幸亏跟着drizzle orm 的教程添加了seed种子(https://hub.nuxt.com/docs/recipes/drizzle#seed-the-database-optional),不然还发现不了,估计要耽搁很多时间啊。

关于数据表UTable的很多操作,还需要从ui/docs/components/content/examples/TableExampleAdvanced.vue at dev · nuxt/ui · GitHub

这个源码里慢慢的学习,加上baidu和google搜索,不然很花时间啊。

还有一个,就是需要安装vscode职能插件,我安装的是 通义灵码,免费的,还是很不错的,可以提醒不同的写法。

关于创建表单数据,并提交,在ui.nuxt.com上的form栏,有不同的数据库实体类的插件,可以挑选https://ui.nuxt.com/components/form#usage

使用方法大同小异。

TypeScript 复制代码
/**
 * zod
 */
import { z } from 'zod';
import type { FormError, FormSubmitEvent } from '#ui/types';

const schema = z.object({
  name: z.string(),
  email: z.string().email('邮箱格式不正确'),
  password: z.string().min(4, '最少4位字符'),
  avatar: z.string(),
});
type Schema = z.output<typeof schema>;

const state = computed(() => {
  return reactive({
    name: z.string().parse('andu' + (info.value + 1)),
    email: z.string().parse('andu' + (info.value + 1) + '@qq.com'),
    password: z.string().parse('andu' + (info.value + 1) + 'password'),
    avatar: z.string().parse('https://picsum.photos/200/200'),
  });
});

const validate = (state: any): FormError[] => {
  const errors = [];
  if (!state.email) errors.push({ path: 'email', message: '必填项' });
  if (!state.password) errors.push({ path: 'password', message: '必填项' });
  return errors;
};

在添加用户的时候,我想自动的给表单赋值,免得一个个手动输入太麻烦了。我的想法是获取数据长度,传给子组件,然后在名称和邮箱名后面数值+1,这样邮箱不就不重复了吗?info就是从父组件传递进来的数据长度值,怎么让表单的数据都自动更新呢?当然是要把它们放到computed里面去了。这样添加数据的是很不用一个个输入的了。

子组件最上角就是获取的数据长度。注意:这个赋值的过程,根本搜不到啊,非常非常意外的是,捣腾了好久,终于,通义灵码给自动推荐出来了!!!好意外!!!

TypeScript 复制代码
name: z.string().parse('andu' + (info.value + 1)),

就是这个z.string().parse()方法,原理是啥,我也不懂,就这么特么的,智能推荐出来了。AI写代码,有的时候是真牛啊。

查询、增加做完了,下面就是删除,删除最简单:

TypeScript 复制代码
const items = (row: any) => [
  [
    {
      label: '编辑',
      icon: 'i-heroicons-pencil-square-20-solid',
    },
    {
      label: '复制',
      icon: 'i-heroicons-document-duplicate-20-solid',
    },
  ],
  [
    {
      label: '存档',
      icon: 'i-heroicons-archive-box-20-solid',
    },
    {
      label: '移动',
      icon: 'i-heroicons-arrow-right-circle-20-solid',
    },
  ],
  [
    {
      label: '删除',
      icon: 'i-heroicons-trash-20-solid',
      click: () => {
        $fetch('/api/users/' + row.id, {
          method: 'DELETE',
        }).then((users: any) => {
          useToast().add({
            title: `${users.id}号删除成功!`,
            description: `
          用户名:${users.name}<br/>
          邮箱:${users.email}
          `,
          });
        });
        refresh();
      },
    },
  ],
];



    <template #actions-data="{ row }">
      <UDropdown :items="items(row)">
        <UButton
          color="gray"
          variant="ghost"
          icon="i-heroicons-ellipsis-horizontal-20-solid"
        />
      </UDropdown>
    </template>
  </UTable>

删除是放在表格最后一列里,这个也是官网的例子有的。只要是路由有id:[id].delete,就需要在$fetch方法中的url里加上row.id

删除后,刷新一下表refresh()。

修改还没做,应该也不难了。这几天卡在好几个点。第一个是日期格式化操作,尝试了几个插件和使用,花了不少时间。还有就是父组件和子组件之间的相互传值,这个应该是vue的基本知识,或者说是vue3的知识,网上多是Vue2的代码,搜索不少时间,慢慢尝试和总结出来传值的规律了。还有就是响应式数据问题,info的获取后,怎么在表单里让它自动更新,变成响应式数据,也是摸索学习了不少时间。最后就是ui.nuxt.com上最后Table的例子的源代码,直接照搬过来,还是没学会怎么用,感觉官方的代码好少,做的时候还多,而且官方的例子源码还不少,这要学起来,那得花多少时间啊......

时间太不够用了......

相关推荐
Amd7945 小时前
Nuxt.js 应用中的 imports:dirs 事件钩子详解
nuxt·目录·模块化·导入·钩子·灵活·可扩展
Amd7941 天前
Nuxt.js 应用中的 imports:context 事件钩子详解
配置·nuxt·导入·钩子·上下文·动态·灵活
Amd7942 天前
Nuxt.js 应用中的 imports:extend 事件钩子详解
组件·模块·nuxt·导入·扩展·钩子·动态
Amd7943 天前
Nuxt.js 应用中的 imports:sources 事件钩子详解
生命周期·配置·模块·nuxt·依赖·扩展·钩子
Amd7945 天前
Nuxt.js 应用中的 pages:extend 事件钩子详解
自定义·生命周期·路由·nuxt·管理·导航·钩子
Amd7946 天前
Nuxt.js 应用中的 builder:watch 事件钩子详解
生命周期·开发·文件·nuxt·构建·钩子·变化
andux6 天前
nuxt自动部署nuxthub之数据库使用sqlite,cloudflare上叫D1
sqlite·nuxt
Amd7947 天前
Nuxt.js 应用中的 builder:generateApp 事件钩子详解
生命周期·应用·配置·优化·nuxt·生成·钩子
Amd7948 天前
Nuxt.js 应用中的 build:manifest 事件钩子详解
自定义·优化·nuxt·控制·清单·构建·钩子