分享项目 - Vue3 + TS + element-ui-plus 项目 -- Table表格表单

news2024/11/22 21:52:35

文章目录

  • 前言
  • 项目地址以及怎么阅读别人的代码
  • 整体代码
    • 分页数据作者是怎么处理的 usePagination
    • 顺藤摸瓜找到 api 接口的封装
    • api 接口再往底层找全局请求封装与请求拦截器 service.ts

前言

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

通过该文章可以学习到 :

  • element-ui-plus 的表单、表格等组件的使用
  • 怎么阅读他人的代码、怎么写出优雅炫酷的代码
  • api 请求以及 api 请求拦截器、api 全局请求封装等知识点

在这里插入图片描述

项目地址以及怎么阅读别人的代码

我们来看一下具体代码是怎么实现的,我读别人的代码喜欢先看一下大体目录结构、然后从页面功能入手,然后在 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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360909.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Springboot @Test 给Controller接口 写 单元测试

前言 最近有小伙伴问到怎么给 controller的接口写单元测试。 单元测试是开发必不可少的一个环节。 既然有人问到了&#xff0c;那我觉得可能不止一个人不会&#xff0c;那就按照惯例&#xff0c;出手。 正文 内容&#xff1a; 主要是get 和 post 两种请求方式的接口 的 单元测…

centos7 开机自启动自定义脚本

centos7 开机自启动自定义脚本背景配置自启动jar1.首先书写自启动脚本2.在rc.local中加入脚本reboot测试docker版本的自启动背景 项目中有遇到2个问题&#xff0c; 1&#xff1a; 使用java启动jar包 2&#xff1a; docker容器中自启动个服务。 这2个都要使用linux的开机自启动问…

AcWing 1017. 怪盗基德的滑翔翼

怪盗基德是一个充满传奇色彩的怪盗&#xff0c;专门以珠宝为目标的超级盗窃犯。而他最为突出的地方&#xff0c;就是他每次都能逃脱中村警部的重重围堵&#xff0c;而这也很大程度上是多亏了他随身携带的便于操作的滑翔翼。有一天&#xff0c;怪盗基德像往常一样偷走了一颗珍贵…

【图像分类】卷积神经网络之LeNet5网络模型实现MNIST手写数字识别

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在上一篇博文中我们对LeNet5网络模型的结构进行了剖析,本篇博文,我们将使用PyTorch搭建LeNet5实现MNIST手写数字…

使用vmware制作云平台redhat7.9镜像模板

一、概述 1.1 redhat7.9 定制镜像上传到云平台。 这个制作镜像得方式适用于多种iso 镜像。 将iso 镜像通过vmware 创建出一台虚机&#xff0c;对虚机做一些基础配置。在虚机上安装kvm 虚拟化得工具&#xff0c; 将iso 镜像在导入虚机种通过kvm创建一下虚机&#xff0c; 虚机创…

Embedding 理解

Word Embedding 单词表示最简单的是 one-hot 但是它的缺点是 矩阵表示过于稀疏&#xff0c;占用空间对相关的词语无法得知它们的含义是相近的。 Word Embedding 解决了上述两个缺点&#xff0c;一个 Word Embedding 直观的例子如下图所示。 每个维度表示一个特征&#xff0…

简述操作系统的文件系统

前言 文件系统是操作系统中负责管理持久数据的子系统&#xff0c;将用户的文件保存在硬盘等硬件设备中&#xff0c;即使断电了数据也不会丢失。 对于用户而言&#xff0c;文件是存储的最小单位&#xff0c;再少的数据也需要以文件的形式存储在外部存储器中。以硬盘为例&#…

金三银四,我不允许你们不知道这些软件测试面试题

01、您所熟悉的测试用例设计方法都有哪些&#xff1f;请分别以具体的例子来说明这些方法在测试用例设计工作中的应用。 答&#xff1a;有黑盒和白盒两种测试种类&#xff0c;黑盒有等价类划分法&#xff0c;边界分析法&#xff0c;因果图法和错误猜测法。白盒有逻辑覆盖法&…

DataX及DataX-Web

大数据Hadoop之——数据同步工具DataX数据采集工具-DataX datax详细介绍及使用 一、概述 DataX 是阿里云DataWorks数据集成的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、…

前端将base64图片转换成file文件

1、base64转成file具体代码 // base64图片转file的方法&#xff08;base64图片, 设置生成file的文件名&#xff09;function base64ToFile(base64, fileName) {// 将base64按照 , 进行分割 将前缀 与后续内容分隔开let data base64.split(,);// 利用正则表达式 从前缀中获取图…

PAT 甲级 1002 python 测试点1未通过

题目&#xff1a; 思路&#xff1a; 1注意多项式非零项的数目 2两项相加为0时不输出 3 测试点1未通过 代码&#xff1a; C{} Alist(input().split()) Blist(input().split())count0 for i in range(int(A[0])):C[A[i*21]]float(A[i*22])count count 1for i in range(int(B…

支付宝支付功能使用

1、进入“蚂蚁金服开放平台” https://open.alipay.com/https://open.alipay.com/ 2、下载支付宝官方 demo&#xff0c;进行配置和测试 文档地址 手机网站支付 DEMO &#xff5c; 网页&移动应用支付宝文档中心https://opendocs.alipay.com/open/02no47 demo下载 网页…

如何使用ngxin的 upstream

1.引言&#xff1a; 1.1反向代理&#xff1a; 反向代理是充当Web服务器网关的代理服务器。当您将请求发送到使用反向代理的Web服务器时&#xff0c;他们将先转到反向代理&#xff0c;由该代理将确定是将其路由到Web服务器还是将其阻止。 这意味着有了反向代理&#xff0c;您…

Java Stream流1

目录1 Stream流1.1 Stream流的生成方式1.2 Stream流中间操作之 filter1.3 Stream流中间操作之 limit 和 skip1.4 Stream流中间操作之 concat 和 distinct1 Stream流 体验Stream流需求 package test;import java.lang.reflect.Array; import java.util.ArrayList;public clas…

Arduino IDE搭建Heltec开发板开发环境

Arduino IDE搭建Heltec开发板开发环境Heltec开发板开发环境下载与搭建Arduino IDE下载与安装搭建Heltec开发板的开发环境添加package URL方法通过Git的方法安装离线安装Heltec开发板开发环境下载与搭建 Arduino IDE下载与安装 Heltec的ESP系列和大部分的LoRa系列开发板都是用A…

2.Gin内容介绍

目录 参考 主要内容 关于Web 创建项目 为什么要用框架 Gin框架介绍 Gin框架安装与使用 安装 第一个Gin示例&#xff1a; RESTful API Gin渲染 HTML渲染 自定义模板函数 静态文件处理 使用模板继承 补充文件路径处理 JSON渲染 XML渲染 YMAL渲染 protobuf渲染…

【Java基础 下】 024 -- 集合进阶(双列集合、HashMap、LinkedHashMap、TreeMap、Collections)

学习内容&#xff1a; 目录 一、双列集合 1、双列集合的特点 2、双列集合的常见API 示例代码 3、Map的遍历方式 ①、第一种遍历方式&#xff1a;键找值&#xff08;keySet&#xff09; ②、第二种遍历方式&#xff1a;键值对&#xff08;entrySet&#xff09; ③、第三种遍历方…

LeetCode 周赛 333,你管这叫 Medium 难度?

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 提问。 大家好&#xff0c;我是小彭。 上周是 LeetCode 第 333 场周赛&#xff0c;你参加了吗&#xff1f;这场周赛质量很高&#xff0c;但难度标得不对&#xff0c;我真的会谢。算法…

深度学习在视频多目标跟踪中的应用综述

文章目录摘要1、简介2、MOT:算法、指标和数据集2.1、MOT算法简介2.2、指标经典的指标完整的MOT指标ID 分数2.3、基准数据集3、MOT中的深度学习3.1、深度学习中的检测步骤3.1.1、Faster R-CNN3.1.2、SSD3.1.3、Other detectors3.1.4、cnn在检测步骤中的其他用途3.2、深度学习在特…

Git的常用命令

1&#xff1a;软件安装1.1&#xff1a;Git下载与安装百度上搜索Git官网&#xff1a;https://git-scm.com/下载&#xff1a;https://git-scm.com/download/win下载Git安装程序&#xff0c;双击安装 Git-2.9.3.2-64-bit.exe配置环境变量path 使用git --version查看 git 是否安装成…