位置: IT常识 - 正文

分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单(分享项目成果)

编辑:rootadmin
分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单 文章目录前言项目地址以及怎么阅读别人的代码整体代码分页数据作者是怎么处理的 usePagination顺藤摸瓜找到 api 接口的封装api 接口再往底层找全局请求封装与请求拦截器 service.ts前言

推荐整理分享分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单(分享项目成果),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:vue技术分享,分享项目的说说,分享项目话术,分享项目话术,分享项目经验,分享项目经验,分享项目要注意什么,分享项目要注意什么,内容如对您有帮助,希望把文章链接给更多的朋友!

今天看一个 ts 项目的 table 模块,亲身体验这是公司后台管理系统一定会使用到的,也是最常使用到的,这个项目对新手很友好,毕竟是一个相对来说比较空的项目模板,对于我来说就是一个学习的记录,一些技术的分享,手把手告知新手别人的代码怎么读,甚至还能帮该开源项目作者获取一些热度,我很乐于做这样的事情(已经争得原作者许可,感谢 🤓)项目地址:V3 Admin Vite

通过该文章可以学习到 :

element-ui-plus 的表单、表格等组件的使用怎么阅读他人的代码、怎么写出优雅炫酷的代码api 请求以及 api 请求拦截器、api 全局请求封装等知识点项目地址以及怎么阅读别人的代码分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单(分享项目成果)

我们来看一下具体代码是怎么实现的,我读别人的代码喜欢先看一下大体目录结构、然后从页面功能入手,然后在 html 中找到该组件,然后查看该组件使用的方法等,一直相连关联到底层封装代码(或者直接看脚本逻辑,从脚本逻辑入手,看大家习惯)

功能、底层封装、页面结构等等知道了,自然而然就通了

我一步一步就标注了我对该代码的思考,希望对于大家有所帮助

整体代码<script lang="ts" setup>import { reactive, ref, watch } from "vue"import { createTableDataApi, deleteTableDataApi, updateTableDataApi, getTableDataApi } from "@/api/table"import { type FormInstance, type FormRules, ElMessage, ElMessageBox } from "element-plus"import { Search, Refresh, CirclePlus, Delete, Download, RefreshRight } from "@element-plus/icons-vue"import { usePagination } from "@/hooks/usePagination"// 加载状态,这也是 element-ui-plus 的一种加载方法,可以查看 html 元素并访问 element-ui-plus 官网来找到该变量有什么用处// 定义 loading 为响应式状态值,ts 限制为布尔类型const loading = ref<boolean>(false)// 自己封装的页面功能,可以转到 对应 src目录下 hooks 文件夹中的 usePagination 中查看对应方法的功能// 相应代码在本文章下面,可对应查看或者直接去 GitHub 下载宝藏博主的源码码进行查看const { paginationData, handleCurrentChange, handleSizeChange } = usePagination()//#region 增// 我们在 html 代码中可以看到 是使用在 el-dialog 组件的 v-module 属性上的,我们可以查看 element-ui-plus 文档查看该功能// v-model 控制这该组件是否显示const dialogVisible = ref<boolean>(false)// 表单对象实例 ts限制为表单实例 或 null// 在新增用户判断时需要使用到,被绑定在 表单的 ref 值上const formRef = ref<FormInstance | null>(null)// 表单输入值const formData = reactive({ username: "", password: ""})// 定义表单验证规则并使用 ts 进行类型规范const formRules: FormRules = reactive({ username: [{ required: true, trigger: "blur", message: "请输入用户名" }], password: [{ required: true, trigger: "blur", message: "请输入密码" }]})// 创建新用户/修改用户 方法const handleCreate = () => { // 判断表单实例是否存在 // 因为 validate 是 element-ui-plus 表单上的一个方法,所以需要使用到表单实例才可以使用该方法,现在我们知道了为什么要获取表单实例了 // validate 接收一个回调函数,或返回 Promise,执行之前是有一个前提的,需要表单实例是存在的 formRef.value?.validate((valid: boolean) => { if (valid) { // 如果 valid 存在,那么判断 currentUpdateId 是否为 undefined // currentUpdateId 是否有值决定着用户操作的是新增还是修改 if (currentUpdateId.value === undefined) { // 发起创建 table 请求,携带用户名与用户密码 createTableDataApi({ username: formData.username, password: formData.password }).then(() => { // 数据请求成功之后弹出提示信息 ElMessage.success("新增成功") // 并将弹框设置为不显示 dialogVisible.value = false // 这里的方法在下面 但是从命名就不难看出 这是新增成功之后重新请求一下所有的数据 保证页面数据的最新 getTableData() }) } else { // 前面也有提到 这里是一个炫酷写法,将新增和修改放在一个方法中,执行哪个方法取决于 currentUpdateId 是否有值 // 不得不佩服作者代码写得很棒 updateTableDataApi({ id: currentUpdateId.value, username: formData.username }).then(() => { ElMessage.success("修改成功") dialogVisible.value = false getTableData() }) } // 没有 valid 值,将会退出该方法不执行任何操作 } else { return false } })}// 读到这里就知道 currentUpdateId 是一个关于什么的值了// 我们查到找 html 代码发现当弹窗关闭的时候会触发该方法// 捋一下思路,也就是弹窗关闭,currentUpdateId 值会清空(用户信息也会清空)// 所以我们可以知道用户信息是为了下次打开弹框不会发生之前数据还显示出来的状况// 而 currentUpdateId 则是当前更新 ID ,该值为 undefined 需要执行的是新增,如果当前拥有用户id,那么执行的就是更新const resetForm = () => { currentUpdateId.value = undefined formData.username = "" formData.password = ""}//#endregion//#region 删// row 是当前点击列表项的数据const handleDelete = (row: any) => { ElMessageBox.confirm(`正在删除用户:${row.username},确认删除?`, "提示", { confirmButtonText: "确定", cancelButtonText: "取消", type: "warning" }).then(() => { // deleteTableDataApi 是封装好的 request(ajax) 方法 // 我们可以看看作者是怎么封装数据请求的 deleteTableDataApi(row.id).then(() => { ElMessage.success("删除成功") getTableData() }) })}//#endregion//#region 改// 这里的修改方法只是给到了用户数据以及 当前ID 值,当用户点击确认按钮的时候才会发出真在的数据请求,将该值给到服务端处理// 所以这里只是一些简单的数据修改const currentUpdateId = ref<undefined | string>(undefined)const handleUpdate = (row: any) => { currentUpdateId.value = row.id formData.username = row.username formData.password = row.password dialogVisible.value = true}//#endregion//#region 查// 表格列表数据 -- 好的代码命名一看就知道是什么意思,是非常棒的const tableData = ref<any[]>([])// 输入框框实例const searchFormRef = ref<FormInstance | null>(null)// 输入框输入值const searchData = reactive({ username: "", phone: ""})// 获取所有列表数据进行页面的渲染const getTableData = () => { loading.value = true // 获取列表数据的 API getTableDataApi({ // 前俩个看命名也知道是分页相关值 currentPage: paginationData.currentPage, size: paginationData.pageSize, username: searchData.username || undefined, phone: searchData.phone || undefined }) .then((res) => { // 总数以及列表数据 paginationData.total = res.data.total tableData.value = res.data.list }) .catch(() => { // 如果数据请求发生错误,那么不显示数据列表 tableData.value = [] }) .finally(() => { // 无论请求成功或者失败 不显示加载图标 loading.value = false })}// 查询const handleSearch = () => { // 只有当数据处在第一页的时候才会刷新数据 if (paginationData.currentPage === 1) { getTableData() } // 跳转到第一页 paginationData.currentPage = 1}// 重置const resetSearch = () => { // resetFields 重置该表单项,将其值重置为初始值,并移除校验结果,这也是 element-ui-plus 组件实例上的方法 searchFormRef.value?.resetFields() if (paginationData.currentPage === 1) { getTableData() } paginationData.currentPage = 1}// 刷新表格const handleRefresh = () => { getTableData()}//#endregion/** 监听分页参数的变化 */// 看到这里我们就明白为什么查询和重置页面为什么跳转到第一页就不管了,因为这里在监听着分页参数的变化,这样的完美代码看着是很爽的,为作者点一个大赞watch([() => paginationData.currentPage, () => paginationData.pageSize], getTableData, { immediate: true })</script><template> <div class="app-container"> <el-card v-loading="loading" shadow="never" class="search-wrapper"> <el-form ref="searchFormRef" :inline="true" :model="searchData"> <el-form-item prop="username" label="用户名"> <el-input v-model="searchData.username" placeholder="请输入" /> </el-form-item> <el-form-item prop="phone" label="手机号"> <el-input v-model="searchData.phone" placeholder="请输入" /> </el-form-item> <el-form-item> <el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button> <el-button :icon="Refresh" @click="resetSearch">重置</el-button> </el-form-item> </el-form> </el-card> <el-card v-loading="loading" shadow="never"> <div class="toolbar-wrapper"> <div> <el-button type="primary" :icon="CirclePlus" @click="dialogVisible = true">新增用户</el-button> <el-button type="danger" :icon="Delete">批量删除</el-button> </div> <div> <el-tooltip content="下载"> <el-button type="primary" :icon="Download" circle /> </el-tooltip> <el-tooltip content="刷新表格"> <el-button type="primary" :icon="RefreshRight" circle @click="handleRefresh" /> </el-tooltip> </div> </div> <div class="table-wrapper"> <el-table :data="tableData"> <el-table-column type="selection" width="50" align="center" /> <el-table-column prop="username" label="用户名" align="center" /> <el-table-column prop="roles" label="角色" align="center"> <template #default="scope"> <el-tag v-if="scope.row.roles === 'admin'" effect="plain">admin</el-tag> <el-tag v-else type="warning" effect="plain">{{ scope.row.roles }}</el-tag> </template> </el-table-column> <el-table-column prop="phone" label="手机号" align="center" /> <el-table-column prop="email" label="邮箱" align="center" /> <el-table-column prop="status" label="状态" align="center"> <template #default="scope"> <el-tag v-if="scope.row.status" type="success" effect="plain">启用</el-tag> <el-tag v-else type="danger" effect="plain">禁用</el-tag> </template> </el-table-column> <el-table-column prop="createTime" label="创建时间" align="center" /> <el-table-column fixed="right" label="操作" width="150" align="center"> <template #default="scope"> <el-button type="primary" text bg size="small" @click="handleUpdate(scope.row)">修改</el-button> <el-button type="danger" text bg size="small" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> </div> <div class="pager-wrapper"> <el-pagination background :layout="paginationData.layout" :page-sizes="paginationData.pageSizes" :total="paginationData.total" :page-size="paginationData.pageSize" :currentPage="paginationData.currentPage" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </el-card> <!-- 新增/修改 --> <el-dialog v-model="dialogVisible" :title="currentUpdateId === undefined ? '新增用户' : '修改用户'" @close="resetForm" width="30%" > <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px" label-position="left"> <el-form-item prop="username" label="用户名"> <el-input v-model="formData.username" placeholder="请输入" /> </el-form-item> <el-form-item prop="password" label="密码"> <el-input v-model="formData.password" placeholder="请输入" /> </el-form-item> </el-form> <template #footer> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="handleCreate">确认</el-button> </template> </el-dialog> </div></template><style lang="scss" scoped>.search-wrapper { margin-bottom: 20px; :deep(.el-card__body) { padding-bottom: 2px; }}.toolbar-wrapper { display: flex; justify-content: space-between; margin-bottom: 20px;}.table-wrapper { margin-bottom: 20px;}.pager-wrapper { display: flex; justify-content: flex-end;}</style>分页数据作者是怎么处理的 usePagination

这是分页数据接口规范以及方法定义导出

import { reactive } from "vue"// ts 定义接口 -- 分页数据接口规范interface IDefaultPaginationData { total: number currentPage: number pageSizes: number[] pageSize: number layout: string}// ts 定义接口 -- 合并数据接口规范interface IPaginationData { total?: number currentPage?: number pageSizes?: number[] pageSize?: number layout?: string}/** 默认的分页参数 */const defaultPaginationData: IDefaultPaginationData = { total: 0, currentPage: 1, pageSizes: [10, 20, 50], pageSize: 10, layout: "total, sizes, prev, pager, next, jumper"}export function usePagination(_paginationData: IPaginationData = {}) { /** 合并分页参数 */ // Object.assign()是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并 // 意思就是传过来的值有的话就覆盖,没有就使用默认分页数据,这个处理很完美 const paginationData = reactive(Object.assign({ ...defaultPaginationData }, _paginationData)) /** 改变当前页码 */ const handleCurrentChange = (value: number) => { paginationData.currentPage = value } /** 改变每页显示数据数量 */ const handleSizeChange = (value: number) => { paginationData.pageSize = value } return { paginationData, handleCurrentChange, handleSizeChange }}}顺藤摸瓜找到 api 接口的封装

我们顺着上面发起请求的导出方法找到了这里(这里位于 src 下的 api 文件夹),这是一些简单的接口定义以及 api 接口的封装,等等,好像有一个奇怪的东西,在依赖包中使用的是 axios ,怎么出现了 request ,肯定还有一个整体封装层,并且应该在那里会有一个请求响应拦截器,我们去看看

import { request } from "@/utils/service"interface ICreateTableRequestData { username: string password: string}interface IUpdateTableRequestData { id: string username: string password?: string}interface IGetTableRequestData { /** 当前页码 */ currentPage: number /** 查询条数 */ size: number /** 查询参数 */ username?: string phone?: string}type GetTableResponseData = IApiResponseData<{ list: { createTime: string email: string id: string phone: string roles: string status: boolean username: string }[] total: number}>/** 增 */export function createTableDataApi(data: ICreateTableRequestData) { return request({ url: "table", method: "post", data })}/** 删 */export function deleteTableDataApi(id: string) { return request({ url: `table/${id}`, method: "delete" })}/** 改 */export function updateTableDataApi(data: IUpdateTableRequestData) { return request({ url: "table", method: "put", data })}/** 查 */export function getTableDataApi(params: IGetTableRequestData) { return request<GetTableResponseData>({ url: "table", method: "get", params })}api 接口再往底层找全局请求封装与请求拦截器 service.ts

果然是 😎

import axios, { type AxiosInstance, type AxiosRequestConfig } from "axios"import { useUserStoreHook } from "@/store/modules/user"import { ElMessage } from "element-plus"import { get } from "lodash-es"import { getToken } from "./cache/cookies"/** 创建请求实例 */function createService() { // 创建一个 Axios 实例 const service = axios.create() // 请求拦截 service.interceptors.request.use( (config) => config, // 发送失败 (error) => Promise.reject(error) ) // 响应拦截(可根据具体业务作出相应的调整) service.interceptors.response.use( (response) => { // apiData 是 API 返回的数据 const apiData = response.data as any // 这个 Code 是和后端约定的业务 Code const code = apiData.code // 如果没有 Code, 代表这不是项目后端开发的 API if (code === undefined) { ElMessage.error("非本系统的接口") return Promise.reject(new Error("非本系统的接口")) } else { switch (code) { case 0: // code === 0 代表没有错误 return apiData default: // 不是正确的 Code ElMessage.error(apiData.message || "Error") return Promise.reject(new Error("Error")) } } }, (error) => { // Status 是 HTTP 状态码 const status = get(error, "response.status") switch (status) { case 400: error.message = "请求错误" break case 401: // Token 过期时,直接退出登录并强制刷新页面(会重定向到登录页) useUserStoreHook().logout() location.reload() break case 403: error.message = "拒绝访问" break case 404: error.message = "请求地址出错" break case 408: error.message = "请求超时" break case 500: error.message = "服务器内部错误" break case 501: error.message = "服务未实现" break case 502: error.message = "网关错误" break case 503: error.message = "服务不可用" break case 504: error.message = "网关超时" break case 505: error.message = "HTTP 版本不受支持" break default: break } ElMessage.error(error.message) return Promise.reject(error) } ) return service}/** 创建请求方法 */function createRequestFunction(service: AxiosInstance) { return function <T>(config: AxiosRequestConfig): Promise<T> { const configDefault = { headers: { // 携带 Token Authorization: "Bearer " + getToken(), "Content-Type": get(config, "headers.Content-Type", "application/json") }, timeout: 5000, baseURL: import.meta.env.VITE_BASE_API, data: {} } return service(Object.assign(configDefault, config)) }}/** 用于网络请求的实例 */export const service = createService()/** 用于网络请求的方法 */export const request = createRequestFunction(service)
本文链接地址:https://www.jiuchutong.com/zhishi/297764.html 转载请保留说明!

上一篇:WGAN(Wasserstein GAN)看这一篇就够啦,WGAN论文解读

下一篇:uniapp中获取dom元素的方法,更改dom元素颜色(遇坑记录)(uniapp dom操作)

  • 微信怎么看公交车还有多久到(微信怎么看公交卡余额)

    微信怎么看公交车还有多久到(微信怎么看公交卡余额)

  • 快手极速版红包挂件在哪开启(快手极速版红包领取失败请稍后重试)

    快手极速版红包挂件在哪开启(快手极速版红包领取失败请稍后重试)

  • 红米手机怎么查询真伪(红米手机怎么查询手机是不是新机)

    红米手机怎么查询真伪(红米手机怎么查询手机是不是新机)

  • 电脑下载爱奇艺怎么下载(电脑下载爱奇艺没有图标)

    电脑下载爱奇艺怎么下载(电脑下载爱奇艺没有图标)

  • 华为相机白色框取消(华为相机照相白色框取消)

    华为相机白色框取消(华为相机照相白色框取消)

  • ios12.4无法验证更新(ios15 无法验证)

    ios12.4无法验证更新(ios15 无法验证)

  • 台式电脑屏保快捷键是哪个(台式电脑屏幕保护快捷键)

    台式电脑屏保快捷键是哪个(台式电脑屏幕保护快捷键)

  • 通过微信可以查到对方的手机号码吗(通过微信可以查找手机号码吗)

    通过微信可以查到对方的手机号码吗(通过微信可以查找手机号码吗)

  • 华为手机充电不进电怎么回事(华为手机充电不开机怎么回事)

    华为手机充电不进电怎么回事(华为手机充电不开机怎么回事)

  • xsmax听筒防水吗

    xsmax听筒防水吗

  • 二维码可以随便关注吗(二维码可以随便扫吗会不会有病毒)

    二维码可以随便关注吗(二维码可以随便扫吗会不会有病毒)

  • aaa电池是几号(aaa电池是几号标志)

    aaa电池是几号(aaa电池是几号标志)

  • 什么叫ie8浏览器(ie8ie10浏览器是什么意思)

    什么叫ie8浏览器(ie8ie10浏览器是什么意思)

  • 8g双通道是什么意思(双通道8g内存够用吗)

    8g双通道是什么意思(双通道8g内存够用吗)

  • iphonexs支持wifi6吗(iphonexs支持30w快充吗)

    iphonexs支持wifi6吗(iphonexs支持30w快充吗)

  • iphone隔空投送失败是怎么回事(iphone隔空投送失灵)

    iphone隔空投送失败是怎么回事(iphone隔空投送失灵)

  • 苹果6splusnfc功能在哪(苹果6splusnfc在哪里)

    苹果6splusnfc功能在哪(苹果6splusnfc在哪里)

  • 华为dua一al00是什么型号(dub-al00a华为什么型号)

    华为dua一al00是什么型号(dub-al00a华为什么型号)

  • 美颜相机怎么设置九宫格拍照(美颜相机怎么设置广角模式)

    美颜相机怎么设置九宫格拍照(美颜相机怎么设置广角模式)

  • oppo手机数据怎么转移到内存卡(oppo手机数据怎么导入荣耀手机)

    oppo手机数据怎么转移到内存卡(oppo手机数据怎么导入荣耀手机)

  • vivox23有红外线功能吗(vivox23有红外线遥控空调吗)

    vivox23有红外线功能吗(vivox23有红外线遥控空调吗)

  • 初雪与最后的秋色相会,日本 (© SpontaneousPictures/iStock/Getty Images Plus)(初雪与最后的秋天的区别)

    初雪与最后的秋色相会,日本 (© SpontaneousPictures/iStock/Getty Images Plus)(初雪与最后的秋天的区别)

  • Element-Plus el-col、el-row快速布局

    Element-Plus el-col、el-row快速布局

  • 代征增值税业务的账务处理
  • 公司注册后一直没有营业怎么办
  • 分公司怎么分配股权
  • 个人所得税怎么交
  • 微信支付宝收入计入科目
  • 已付款收不到发票怎么办
  • 小规模进项负数发票需要认证吗
  • 企业走账的会计处理
  • 母公司代子公司付款合法吗
  • 委托加工物资加工费怎么结转
  • 企业安装监控费用怎么做账
  • 财务人员需具备的基本素质和条件是什么
  • 土地增值税清算是什么意思
  • 外购低值易耗品可以抵扣进项税额吗
  • 公司租车税务处理办法
  • 大型超市税收分析报告
  • 待转销项税额会计分录
  • 新个税过了申报期怎么办
  • 注册资本印花税税目是什么
  • 新注册公司季度资产为零
  • 政府补贴是否可享受即征即退优惠政策
  • 员工购买公司股权
  • 海运费怎么做会计分录
  • 总公司签订合同发票开具给分公司是否可以抵扣
  • 生产车间工资计入什么费用科目
  • 公司上市前缩股
  • 宽带调制解调器出现问题怎么解决
  • ThinkPHP中SHOW_RUN_TIME不能正常显示运行时间的解决方法 原创
  • 商会账目
  • 小规模纳税人和一般纳税人哪个好
  • 炫龙笔记本win10系统为什么没有关闭触控板
  • 零售业如何盈利
  • 购进旧设备折旧年限如何计算
  • 逾期未收回包装物押金增值税
  • 驼鹿穿过莫兰山了吗
  • 小规模纳税人低于30万怎么填报申报表
  • ai安装教程2020
  • 宝塔怎么做?
  • python统计字符串长度
  • 增值税专用发票几个点
  • 费用在发生时确认而不考虑支付费用
  • 记账凭证错了
  • mysql中用户和权限的作用
  • mysql skip ssl
  • 开个分公司有啥好处
  • 工资能否当月计提当月发放
  • 应收账款收不回来了应怎么做会计分录
  • 无形资产减值迹象有哪些
  • 冲估价入库怎么冲成本
  • 销售边角料的增值税率
  • 委外研发费用如何入账
  • 有限合伙企业需要缴纳企业所得税吗
  • 产品检测费计入什么二级科目里
  • 软件公司se
  • 旅游发票可以报销入什么明细
  • 资产评估资产如何入帐
  • 收到的赔款罚款怎么做账
  • 材料会计实务操作题答案
  • 房地产企业的土地使用权计入什么科目
  • 废品损失的计算方法及废品损失的范围
  • mysql数据库全量备份
  • sqlserver删除重复
  • win10系统如何将c盘的软件移到d盘
  • xp系统连接共享文件夹
  • windows开始搜索栏
  • win10系统小娜不见了
  • ubuntu下安装deb文件
  • mac识别文字软件
  • macbook如何登录微信
  • linux xim
  • 万能win8pe工具箱怎么用
  • win8安装ie11
  • cocos2d转unity难吗
  • sed删除文件中的目录
  • python对excel操作真的有提高吗
  • 第二章,动态添加按钮(Android)
  • 公职律师
  • 管道运输合同需不需要缴纳印花税
  • 豫麦336
  • 个人养老金没有开立资金账户
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设