Vue3的CRUD模版(附Demo)

目录

前言

用惯Vue2之后,在碰Vue3后,整体还是有所区别

此文主要做一个回顾总结

假设界面如下:

可CRUD,对应的新增 添加一些必选项:

其中数据库的设计如下:

模版

对应需要注意参数位置、初始化表单,重置表单等位置

其首页界面如下:

html 复制代码
<template>
  <ContentWrap>
    <!-- 搜索工作栏 -->
    <el-form
      class="-mb-15px"
      :model="queryParams"
      ref="queryFormRef"
      :inline="true"
      label-width="68px"
    >
      <el-form-item label="企业名称" prop="enterpriseName">
        <el-input
          v-model="queryParams.enterpriseName"
          placeholder="请输入企业名称"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="信用代码" prop="creditCode">
        <el-input
          v-model="queryParams.creditCode"
          placeholder="请输入信用代码"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="注册人" prop="registrant">
        <el-input
          v-model="queryParams.registrant"
          placeholder="请输入注册人"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="联系电话" prop="contactNumber">
        <el-input
          v-model="queryParams.contactNumber"
          placeholder="请输入联系电话"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="创建时间" prop="createTime">
        <el-date-picker
          v-model="queryParams.createTime"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="daterange"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="审批通过与否" prop="status">
        <el-select
          v-model="queryParams.status"
          placeholder="请选择审批通过与否"
          clearable
          class="!w-240px"
        >
          <el-option
            v-for="dict in getStrDictOptions(DICT_TYPE.DSP_PASS_OR_NOT)"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
        <el-button
          type="primary"
          plain
          @click="openForm('create')"
          v-hasPermi="['dangerous:enterprise-registry:create']"
        >
          <Icon icon="ep:plus" class="mr-5px" /> 新增
        </el-button>
        <el-button
          type="success"
          plain
          @click="handleExport"
          :loading="exportLoading"
          v-hasPermi="['dangerous:enterprise-registry:export']"
        >
          <Icon icon="ep:download" class="mr-5px" /> 导出
        </el-button>
      </el-form-item>
    </el-form>
  </ContentWrap>

  <!-- 列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
      <el-table-column label="序号" align="center" prop="id" />
      <el-table-column label="企业名称" align="center" prop="enterpriseName" />
      <el-table-column label="信用代码" align="center" prop="creditCode" />
      <el-table-column label="注册人" align="center" prop="registrant" />
      <el-table-column label="联系电话" align="center" prop="contactNumber" />
      <el-table-column
        label="创建时间"
        align="center"
        prop="createTime"
        :formatter="dateFormatter"
        width="180px"
      />
      <el-table-column label="审批通过与否" align="center" prop="status">
        <template #default="scope">
          <dict-tag :type="DICT_TYPE.DSP_PASS_OR_NOT" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="操作" align="center">
        <template #default="scope">
          <el-button
            link
            type="primary"
            @click="openForm('update', scope.row.id)"
            v-hasPermi="['dangerous:enterprise-registry:update']"
          >
            编辑
          </el-button>
          <el-button
            link
            type="danger"
            @click="handleDelete(scope.row.id)"
            v-hasPermi="['dangerous:enterprise-registry:delete']"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
  </ContentWrap>

  <!-- 表单弹窗:添加/修改 -->
  <EnterpriseRegistryForm ref="formRef" @success="getList" />
</template>

<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { EnterpriseRegistryApi, EnterpriseRegistryVO } from '@/api/dangerous/enterpriseregistry'
import EnterpriseRegistryForm from './EnterpriseRegistryForm.vue'

/** 企业信息 列表 */
defineOptions({ name: 'EnterpriseRegistry' })

const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化

const loading = ref(true) // 列表的加载中
const list = ref<EnterpriseRegistryVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  enterpriseName: undefined,
  creditCode: undefined,
  registrant: undefined,
  contactNumber: undefined,
  createTime: [],
  status: undefined,
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中

/** 查询列表 */
const getList = async () => {
  loading.value = true
  try {
    const data = await EnterpriseRegistryApi.getEnterpriseRegistryPage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}

/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.pageNo = 1
  getList()
}

/** 重置按钮操作 */
const resetQuery = () => {
  queryFormRef.value.resetFields()
  handleQuery()
}

/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
  formRef.value.open(type, id)
}

/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await EnterpriseRegistryApi.deleteEnterpriseRegistry(id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await getList()
  } catch {}
}

/** 导出按钮操作 */
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await EnterpriseRegistryApi.exportEnterpriseRegistry(queryParams)
    download.excel(data, '企业信息.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}

/** 初始化 **/
onMounted(() => {
  getList()
})
</script>

其表单内容如下:

html 复制代码
<template>
  <Dialog :title="dialogTitle" v-model="dialogVisible">
    <el-form
      ref="formRef"
      :model="formData"
      :rules="formRules"
      label-width="100px"
      v-loading="formLoading"
    >
      <el-form-item label="企业名称" prop="enterpriseName">
        <el-input v-model="formData.enterpriseName" placeholder="请输入企业名称" />
      </el-form-item>
      <el-form-item label="信用代码" prop="creditCode">
        <el-input v-model="formData.creditCode" placeholder="请输入信用代码" />
      </el-form-item>
      <el-form-item label="注册人" prop="registrant">
        <el-input v-model="formData.registrant" placeholder="请输入注册人" />
      </el-form-item>
      <el-form-item label="联系电话" prop="contactNumber">
        <el-input v-model="formData.contactNumber" placeholder="请输入联系电话" />
      </el-form-item>
      <el-form-item label="审批通过与否" prop="status">
        <el-radio-group v-model="formData.status">
          <el-radio
            v-for="dict in getStrDictOptions(DICT_TYPE.DSP_PASS_OR_NOT)"
            :key="dict.value"
            :label="dict.value"
          >
            {{ dict.label }}
          </el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
      <el-button @click="dialogVisible = false">取 消</el-button>
    </template>
  </Dialog>
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { FormRules } from 'element-plus'
import { EnterpriseRegistryApi, EnterpriseRegistryVO } from '@/api/dangerous/enterpriseregistry'

/** 企业信息 表单 */
defineOptions({ name: 'EnterpriseRegistryForm' })

const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗

const dialogVisible = ref(false) // 弹窗的是否展示
const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const formType = ref('') // 表单的类型:create - 新增;update - 修改
const formData = ref({
  id: undefined,
  enterpriseName: undefined,
  creditCode: undefined,
  registrant: undefined,
  contactNumber: undefined,
  status: undefined,
})
const formRules = reactive<FormRules>({
  enterpriseName: [{ required: true, message: '企业名称不能为空', trigger: 'blur' }],
  creditCode: [{ required: true, message: '信用代码不能为空', trigger: 'blur' }],
  registrant: [{ required: true, message: '注册人不能为空', trigger: 'blur' }],
  contactNumber: [{ required: true, message: '联系电话不能为空', trigger: 'blur' }],
  status: [{ required: false, message: '联系电话不能为空', trigger: 'blur' }]
})
const formRef = ref() // 表单 Ref

/** 打开弹窗 */
const open = async (type: string, id?: number) => {
  dialogVisible.value = true
  dialogTitle.value = t('action.' + type)
  formType.value = type
  resetForm()
  // 修改时,设置数据
  if (id) {
    formLoading.value = true
    try {
      formData.value = await EnterpriseRegistryApi.getEnterpriseRegistry(id)
    } finally {
      formLoading.value = false
    }
  }
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗

/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
  // 校验表单
  await formRef.value.validate()
  // 提交请求
  formLoading.value = true
  try {
    const data = formData.value as unknown as EnterpriseRegistryVO
    if (formType.value === 'create') {
      await EnterpriseRegistryApi.createEnterpriseRegistry(data)
      message.success(t('common.createSuccess'))
    } else {
      await EnterpriseRegistryApi.updateEnterpriseRegistry(data)
      message.success(t('common.updateSuccess'))
    }
    dialogVisible.value = false
    // 发送操作成功的事件
    emit('success')
  } finally {
    formLoading.value = false
  }
}

/** 重置表单 */
const resetForm = () => {
  formData.value = {
    id: undefined,
    enterpriseName: undefined,
    creditCode: undefined,
    registrant: undefined,
    contactNumber: undefined,
    status: undefined,
  }
  formRef.value?.resetFields()
}
</script>

对应后端的接口传输如下:

js 复制代码
import request from '@/config/axios'

// 企业信息 VO
export interface EnterpriseRegistryVO {
  id: number // 序号
  enterpriseName: string // 企业名称
  creditCode: string // 信用代码
  registrant: string // 注册人
  contactNumber: number // 联系电话
  status: string // 审批通过与否
}

// 企业信息 API
export const EnterpriseRegistryApi = {
  // 查询企业信息分页
  getEnterpriseRegistryPage: async (params: any) => {
    return await request.get({ url: `/dangerous/enterprise-registry/page`, params })
  },

  // 查询企业信息详情
  getEnterpriseRegistry: async (id: number) => {
    return await request.get({ url: `/dangerous/enterprise-registry/get?id=` + id })
  },

  // 新增企业信息
  createEnterpriseRegistry: async (data: EnterpriseRegistryVO) => {
    return await request.post({ url: `/dangerous/enterprise-registry/create`, data })
  },

  // 修改企业信息
  updateEnterpriseRegistry: async (data: EnterpriseRegistryVO) => {
    return await request.put({ url: `/dangerous/enterprise-registry/update`, data })
  },

  // 删除企业信息
  deleteEnterpriseRegistry: async (id: number) => {
    return await request.delete({ url: `/dangerous/enterprise-registry/delete?id=` + id })
  },

  // 导出企业信息 Excel
  exportEnterpriseRegistry: async (params) => {
    return await request.download({ url: `/dangerous/enterprise-registry/export-excel`, params })
  },
}
相关推荐
gis开发之家11 小时前
《Vue3 从入门到大神33篇》Vue3 源码详解(三):响应式核心思想——从 Object.defineProperty 到 Proxy
前端·javascript·vue.js·vue3·vue3源码
gis开发之家2 天前
《Vue3 从入门到大神32篇》Vue3 源码详解(二):从 GitHub Clone 到断点调试,手把手搭建源码阅读环境
vue.js·前端框架·github·vue3·vue3源码
gis开发之家2 天前
vue3直击架构痛点 依赖倒置与接口隔离——打造坚如磐石的插件与高阶组件(HOC)模式
javascript·架构·typescript·前端框架·vue3
gis开发之家4 天前
《Vue3 从入门到大神29篇》单元测试与 E2E 测试 —— 保障 Vue3 项目的质量
前端·javascript·vue.js·单元测试·前端框架·vue3
雨季mo浅忆5 天前
上传文件夹
前端·vue3
xrl20127 天前
若依工作流主流方案对接
ruoyi·camunda·ruoyiflow
蜡台7 天前
uniapp vue2 转 vue3
前端·javascript·uni-app·vue3·vue2
随风一样自由8 天前
【前端独角兽】刚进入前端领域的前端开发用jQuery还是Vue3?
前端·javascript·vue3·jquery
gis开发之家10 天前
《Vue3 从入门到大神25篇》手撕 Vue3 响应式原理 —— 手写 Mini-Vue
前端·javascript·vue.js·前端框架·vue3
gis开发之家10 天前
vue3组合式函数(Composables)的艺术——将业务逻辑从UI组件中连根拔起
前端·javascript·vue.js·前端框架·vue3·前端工程化·前端架构