位置: 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操作)

  • 电脑截屏的三种方法(电脑截屏的三种方法 技巧)

    电脑截屏的三种方法(电脑截屏的三种方法 技巧)

  • 一注册陌陌就设备异常(陌陌怎么一注册就异常)

    一注册陌陌就设备异常(陌陌怎么一注册就异常)

  • 拼多多待成团怎么处理(拼多多待成团怎么申请退款)

    拼多多待成团怎么处理(拼多多待成团怎么申请退款)

  • opporeno2呼吸灯在哪里(opporeno6呼吸灯)

    opporeno2呼吸灯在哪里(opporeno6呼吸灯)

  • 群主如何知道谁退出了(群聊的群主怎么看是谁)

    群主如何知道谁退出了(群聊的群主怎么看是谁)

  • 华为nova6se侧边指纹在哪里(华为nova6se侧边指纹坏了)

    华为nova6se侧边指纹在哪里(华为nova6se侧边指纹坏了)

  • 苹果手机到晚上背景变黑怎么办(苹果手机到晚上没有提示)

    苹果手机到晚上背景变黑怎么办(苹果手机到晚上没有提示)

  • 淘宝商家电话在哪里找(淘宝电话商家电话)

    淘宝商家电话在哪里找(淘宝电话商家电话)

  • 抖音限流是指所有视频吗(抖音限流是指所有人吗)

    抖音限流是指所有视频吗(抖音限流是指所有人吗)

  • 内存储器包括什么(内存储器包括哪两部分)

    内存储器包括什么(内存储器包括哪两部分)

  • 华为手环4如何绑定支付宝(华为手环4如何充电)

    华为手环4如何绑定支付宝(华为手环4如何充电)

  • 迅雷没有ios版本的么(迅雷没有ios版本怎么办)

    迅雷没有ios版本的么(迅雷没有ios版本怎么办)

  • 快手小店服务费是多少(快手小店服务费收取标准)

    快手小店服务费是多少(快手小店服务费收取标准)

  • 探探怎么申请退款流程(探探怎么申请退款VIP)

    探探怎么申请退款流程(探探怎么申请退款VIP)

  • iphone11pro max颜色怎么选(iphone11pro max颜色对比)

    iphone11pro max颜色怎么选(iphone11pro max颜色对比)

  • 神舟笔记本开机键在哪(神舟笔记本开机灯闪一下就灭了)

    神舟笔记本开机键在哪(神舟笔记本开机灯闪一下就灭了)

  • 摩擦设备封停怎么回事(摩擦设备封停怎么解决)

    摩擦设备封停怎么回事(摩擦设备封停怎么解决)

  • 手机号异常是怎么回事(手机号异常是怎么恢复)

    手机号异常是怎么回事(手机号异常是怎么恢复)

  • 962464用九键打出来是什么意思

    962464用九键打出来是什么意思

  • 苹果手机如何电话同步录音(苹果手机如何电视投屏设置方法)

    苹果手机如何电话同步录音(苹果手机如何电视投屏设置方法)

  • 安卓手机拦截功能在哪(安卓手机拦截功能在哪里关闭)

    安卓手机拦截功能在哪(安卓手机拦截功能在哪里关闭)

  • p30可以插内存卡吗(华为p30可以插内存卡吗)

    p30可以插内存卡吗(华为p30可以插内存卡吗)

  • 【2022-05-31】JS逆向之易企秀(逆算法怎么算)

    【2022-05-31】JS逆向之易企秀(逆算法怎么算)

  • php使用array_diff去除元素(php使用while循环计算1到100的和)

    php使用array_diff去除元素(php使用while循环计算1到100的和)

  • 退个税手续费如何申报
  • 白酒赠品赠什么方案
  • 什么是边际税率高
  • 资金账簿印花税每年都要交吗?
  • 契税是什么税,怎么算的
  • 交了9个点的工程增值税不可以抵扣吗
  • 收到财政局拨款填到现金流量表哪一项
  • 公司买的电器可以抵扣吗
  • 临时设施摊销方案怎么写
  • 电子发票上的字体大小
  • 长期待摊费用摊销年限规定
  • 货币资金项目应根据账户的期末余额合计填列
  • 退货未开发票如何做账
  • 作为福利手段福利概念的三个条件是
  • 成品油购进数据未同步怎么办
  • 开票金额和实际金额不一致
  • 增值税是先交税还是先开票
  • 承包外单位食堂会计怎么做账
  • 工资未发放是否可以仲裁
  • 抵扣勾选和退税勾选选错了怎么办
  • 企业对外投资需要注意的事项
  • 关于进项税额转出的规定
  • 人工费增值税的税率是多少?
  • 企业为员工租房后的事故处理
  • 仓库多入库怎么退
  • 合伙企业分配比例税法规定
  • 跨区经营的税务政策
  • 委外加工企业怎么样降本
  • 哪些发票公司可以抵扣
  • 审计报告与汇缴报告
  • 经费支出渠道
  • 分级核算下的建议有哪些
  • ESP32-CAM AI THINKER 引脚排列:GPIO 用法说明
  • windows7安装nodejs14
  • 专项应付款费用化
  • php中session什么意思
  • 卢塞恩小镇瑞士
  • 本年利润怎么结转到未分配利润分录
  • 政府基金如何开立账户
  • 解决跨域问题的注解
  • 低调低调
  • 最新前端技术
  • 会计中利得的定义
  • get_module_base
  • 新成立的公司的搭建费可以申请补助吗?
  • 工会经费计提的会计分录
  • 小规模资产负债表怎么填写
  • 发票金额大于付款金额怎么做分录
  • sqlserver四舍五入到整数
  • 运输行业油费怎么入账
  • 建筑企业的收入特点有哪些
  • 公司什么项目
  • 撰写广告
  • 财务报表中存货为负数是什么意思
  • 公司注销实收资本怎么处理
  • 简易计税方法的适用范围
  • 项目提前竣工借款合法吗
  • 公司参赛获奖交啥税
  • 报告日前损益调整
  • 弥补企业以前年度亏损 顺序
  • 未分配利润高怎么解释
  • 如何进行企业建账
  • mysql字段超长
  • win7一直弹广告怎么办
  • 7.0 Release i386 DVD iso 下载
  • vista和win10
  • win10右下角功能列表不见了
  • 在Linux系统中安装pacman
  • android开源软件
  • eclipse开发安卓app实例
  • vue.js 2.x
  • python 转义字符处理
  • shell监控端口状态
  • js中scrollHeight,scrollWidth,scrollLeft,scrolltop等差别介绍
  • javascript总结笔记
  • unity 2021.2
  • fiori开发
  • javascript核心技术
  • jquery获取元素父级body高度
  • python爬虫抓取数据的步骤
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

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

    友情链接: 武汉网站建设