• vue3 使用setup语法糖实现分类管理


    setup语法糖简介

    直接在 script 标签中添加 setup 属性就可以直接使用 setup 语法糖了。

    使用 setup 语法糖后,不用写 setup 函数,组件只需要引入不需要注册,属性和方法也不需要再返回,可以直接在 template 模板中使用。

    setup语法糖中新增的api

    • defineProps:子组件接收父组件中传来的 props
    • defineEmits:子组件调用父组件中的方法
    • defineExpose:子组件暴露属性,可以在父组件中拿到

    模块简介

    本次模块使用 vue3+element-plus 实现一个新闻站的后台分类管理模块,其中新增、编辑采用对话框方式公用一个表单

    分类模块路由

    添加分类模块的路由

    import { createRouter, createWebHistory } from "vue-router";
    import Layout from "@/views/layout/IndexView";
    
    const routes = [
      {
        path: "/sign_in",
        component: () => import("@/views/auth/SignIn"),
        meta: { title: "登录" },
      },
      {
        path: "/",
        component: Layout,
        children: [
          {
            path: "",
            component: () => import("@/views/HomeView"),
            meta: { title: "首页" },
          },
          // 分类管理
          {
            path: "/categories",
            component: () => import("@/views/categories/ListView"),
            meta: { title: "分类列表" },
          },
        ],
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(process.env.BASE_URL),
      routes,
    });
    
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    分类列表组件

    views/categories/ListView.vue

    <template>
      <div>
        <el-breadcrumb separator="/">
          <el-breadcrumb-item :to="{ path: '/' }">首页el-breadcrumb-item>
          <el-breadcrumb-item>内容管理el-breadcrumb-item>
          <el-breadcrumb-item>分类列表el-breadcrumb-item>
        el-breadcrumb>
    
        <el-divider />
    
        <el-button type="primary">新增el-button>
    
        <el-table :data="categories" style="width: 100%" class="set-top">
          <el-table-column prop="id" label="编号" width="180" />
          <el-table-column label="名称" width="180">
            <template #default="scope">
              <el-tag>{{ scope.row.name }}el-tag>
            template>
          el-table-column>
          <el-table-column label="排序" width="180">
            <template #default="scope">
              {{ scope.row.sort }}
            template>
          el-table-column>
          <el-table-column label="创建日期" width="180">
            <template #default="scope">
              {{ formatter(scope.row.createdAt) }}
            template>
          el-table-column>
          <el-table-column label="操作">
            <template #default="scope">
              <el-button size="small" @click="handleEdit(scope.row)"
                >编辑
              el-button>
              <el-popconfirm
                title="确定要删除么?"
                confirm-button-text="确定"
                cancel-button-text="取消"
                @confirm="handleDelete(scope.row)"
              >
                <template #reference>
                  <el-button size="small" type="danger">删除 el-button>
                template>
              el-popconfirm>
            template>
          el-table-column>
        el-table>
      div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    获取分类列表数据

    <script setup>
      import { ref } from "vue"; // 5、导入 ref
      import { fetchCategoryList } from "@/api/categories"; // 6、导入接口 api
      import moment from "moment"; // 7、导入 moment 包
      import { ElMessage, ElNotification } from "element-plus"; // 9、导入消息通知包
      
      // 4、定义分类列表数组
      const categories = ref([]);
    
      // 1、获取分类列表数据
      const init = async () => {
          const res = await fetchCategoryList();
          // 3、赋值
          categories.value = res.data.categories;
      };
    
      // 2、调用 init 方法
      init();
    
      // 8、时间格式化
      const formatter = (date) => {
          if (!date) {
            return "";
          }
          moment.locale("zh-cn");
          return moment(date).format("LL");
      };
    
      const handleEdit = (row) => {
        console.log(row);
      };
      
      // 10、点击删除按钮
      const handleDelete = (row) => {
          try {
              const res = await deleteCategory(row.id);
              if (res.code === 20000) {
                  init();
                  ElNotification({
                      title: "成功",
                      message: res.message,
                      type: "success",
                  });
              }
          } catch (e) {
              if (e.Error) {
                ElMessage.error(e.Error);
              }
          }
      };
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    分类表单组件

    1、新建 src/views/categories/components/CategoryForm.vue

    <template>
      <el-dialog v-model="dialogFormVisible" title="新增分类">
        <el-form :model="form" :rules="rules" ref="ruleFormRef">
          <el-form-item label="名称" :label-width="formLabelWidth" prop="name">
            <el-input v-model="form.name" autocomplete="off" size="large" />
          el-form-item>
          <el-form-item label="排序" :label-width="formLabelWidth" prop="sort">
            <el-input v-model.number="form.sort" autocomplete="off" size="large" />
          el-form-item>
        el-form>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取消el-button>
            <el-button type="primary" @click="submitForm(ruleFormRef)">立即创建el-button>
          span>
        template>
      el-dialog>
    template>
    
    <script setup>
      import { reactive, ref } from "vue";
    
      // 1、定义对话框属性,默认值为 false
      const dialogFormVisible = ref(false);
    
      // 2、定义表单对象和属性
      const form = ref({
        name: "",
        sort: 0,
      });
      
      const formLabelWidth = "140px";
    
      // 3、表单验证
      const ruleFormRef = ref();
      const rules = reactive({
        name: [
          { required: true, message: "请输入分类名称", trigger: "blur" },
          { min: 2, max: 20, message: "长度在 2 ~ 20 位", trigger: "blur" },
        ],
        sort: [
          { required: true, message: "请输入排序", trigger: "blur" },
          { type: "number", message: "排序必须为数字值" },
        ],
      });
      
      // 4、表单提交
      const submitForm = async (formEl) => {
        await formEl.validate(async (valid) => {
          if (valid) {
            console.log('submit');
          }
        }
      } 
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    2、在 categories/ListView.vue 中引入上述表单组件

    <template>
      <div>
        .
        .
        
        <CategoryForm ref="dialogShow" />
      div>
    template>
    
    <script setup>
      import CategoryForm from "./components/CategoryForm"; // 导入对话框组件
    script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ref=“dialogShow”:给表单对话框起别名

    3、给新增、编辑按钮分别绑定事件,点击后弹出对话框

    <el-button type="primary" @click="handleCreate">新增el-button>
    
    <el-button size="small" @click="handleEdit(scope.row)">编辑el-button>
    
    • 1
    • 2
    • 3
    // 点击新增按钮触发子组件的 showForm 方法,并传参 create,代表新增
    const dialogShow = ref(null);
    const handleCreate = async () => {
      dialogShow.value.showForm("create");
    };
    
    // 点击编辑按钮钮触发子组件的 showForm 方法,并传参 edit 和编辑所需的 id 值,代表编辑
    const handleEdit = async (row) => {
      dialogShow.value.showForm("edit", { id: row.id });
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、在表单组件中

    <script setup>
      // 显示对话框
      const showForm = async (type, data) => {
        console.log(type);
        console.log(data);
      }
    </script>  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试:此时点击新增或编辑按钮,发现无法触发 showForm 方法。原因在于我们要在父组件中调用子组件的方法,需导出子组件的方法后才能调用

    <script setup>
      import { defineExpose } from "vue";
    
      defineExpose({
        showForm,
      });
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此时再次点击新增或编辑按钮,发现已经拿到了 type 和 data 的值了。

    5、完成新增和编辑的对话框正常显示

    // 定义表单类型的默认值为 create
    const formType = ref("create");
    
    // 完成新增和编辑正常对话框显示
    const showForm = async (type, data) => {
      dialogFormVisible.value = true;
      formType.value = type;
      if (type == "create") {
        form.value = {};
      } else {
        fetchCategory(data.id).then((res) => {
          form.value = res.data.category;
        });
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    对话框字体显示

    根据 formType 的值判断显示新增或编辑

    <template>
      <el-dialog
        v-model="dialogFormVisible"
        :title="formType == 'create' ? '新增分类' : '编辑分类'"
      >
        <el-form :model="form" :rules="rules" ref="ruleFormRef">
          <el-form-item label="名称" :label-width="formLabelWidth" prop="name">
            <el-input v-model="form.name" autocomplete="off" size="large" />
          el-form-item>
          <el-form-item label="排序" :label-width="formLabelWidth" prop="sort">
            <el-input v-model.number="form.sort" autocomplete="off" size="large" />
          el-form-item>
        el-form>
        <template #footer>
          <span class="dialog-footer">
            <el-button @click="dialogFormVisible = false">取消el-button>
            <el-button type="primary" @click="submitForm(ruleFormRef)">{{
              formType == "create" ? "立即创建" : "立即更新"
            }}el-button>
          span>
        template>
      el-dialog>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    完成新增和编辑功能

    import {
      createCategory,
      fetchCategory,
      updateCategory,
    } from "@/api/categories";
    import { ElMessage, ElNotification } from "element-plus";
    
    // 表单提交
    const submitForm = async (formEl) => {
      await formEl.validate(async (valid) => {
        if (valid) {
          let res;
          try {
            if (formType.value == "create") {
              res = await createCategory(form.value);
            } else {
              res = await updateCategory(form.value.id, form.value);
            }
            if (res.code === 20000) {
              ElNotification({
                title: "成功",
                message: res.message,
                type: "success",
              });
              dialogFormVisible.value = false;
            }
          } catch (e) {
            if (e.Error) {
              ElMessage.error(e.Error);
            }
          }
        }
      });
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    当新增或编辑表单提交后,新的数据要同步渲染到页面,根据思路,我们需要调用父组件的 init 方法即可,所以这里涉及到子组件调用父组件的方法

    修改父组件引用子组件的代码,增加 @init 事件,绑定 init 方法

    
    <CategoryForm ref="dialogShow" @init="init" />
    
    • 1
    • 2

    在子组件中调用,注意注释中的代码

    // eslint-disable-next-line no-undef
    const emit = defineEmits(["init"]); // 引入父组件的 init 方法
    const submitForm = async (formEl) => {
      await formEl.validate(async (valid) => {
        if (valid) {
          let res;
          try {
            if (formType.value == "create") {
              res = await createCategory(form.value);
            } else {
              res = await updateCategory(form.value.id, form.value);
            }
            if (res.code === 20000) {
              ElNotification({
                title: "成功",
                message: res.message,
                type: "success",
              });
              dialogFormVisible.value = false;
              emit("init"); // 调用 init
            }
          } catch (e) {
            if (e.Error) {
              ElMessage.error(e.Error);
            }
          }
        }
      });
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
  • 相关阅读:
    数据结构--希尔排序
    社区版idea找不到Test
    【博客450】OpenFlow学习
    Java中常用指令和工具
    LeetCode-432. All O`one Data Structure [C++][Java]
    解决Linux Ubuntu上安装RabbitMQ服务后的公网远程访问问题,借助cpolar内网穿透技术
    JavaScript Reference Type 解读
    远程创建分支本地VScode看不到分支
    Linux 系统上卸载 Docker
    flink安装与基础测试
  • 原文地址:https://blog.csdn.net/huangdj321/article/details/126366489