vue3导入导出

vue3+Ant Design Vue Pro +typeScript 封装上传组件以及上传hooks (useImport)

  1. 后台管理常见的导入导出功能页面展示

后台管理基本上都会有导入导出功能,由于每个页面基本上都会有,而且写的时候都会写很多重复的代码,所以我们需要将导入导出按钮封装成一个组件去使用

vue3导入导出_第1张图片

  1. 使用a-upload注意事项

2.1:用过上传组件的基本都知道,上传无非就是包括了直接使用action上传或者使用自定义函数上传,比如后端需要formData格式,我们此时需要new fromData去传参,由于业务需求我们公司使用的直接用action上传,也就意味着直接传URL给a-upload的action属性即可

2.2:需要注意的是,上传时必须在请求头里携带上token,同时必须要在上传过程中做一些必要的校验(例如:只能上传office等),类似导入之类的业务需求一般都是上传excel,所以我们必须要限制文件上传的类型以及文件的大小(大小一般由产品决定)

2.3:导入需求一般需要点击导入按钮,弹出弹窗,然后由用户下载模板,然后填写完成后点击上传,然后会触发上传的before-upload钩子,如果通过校验,则为done状态,如果未通过,则为error状态,这时我们需要toast提示用户格式不正确XXX之类的提示语

vue3导入导出_第2张图片

2.4:用户在上传后会有成功失败2种状态,如果失败需要查看失败原因等需求(视自己需求决定)

3.子组件代码演示






3.1:上述代码可以看出,在上传过程中可能会有额外的参数传给a-upload组件的data属性即可,如果需要修改,可以对外暴漏函数去调用传值修改即可。

3.2: 上传文件路径以及下载模板等一般需要后端配合,我们这里的做法是路径会传一个字符串到子组件里,下载模板需要后端返回URL,前端直接下载文件即可,所以父组件需要传文件上传路径以及下载文件的请求到子组件

3.3: 上传结束后,如果有失败状态的文件,也需要调用后端接口下载excel文件

4.父组件使用

4.1:父组件代码

import templateImport from '@/components/template-import/index.vue'




const goodsImportData = () => init.getConfig().api + 'baseservice/goods/importData' // 商品文件上传地址

const importData = reactive({
  data: {
    // 控制导入弹窗的打开
    importShow: false,
    // 上传文件地址
    upLoadUrl: '',
  },
})

  /**
   * @method 导入
   */
const handleImport = () => {
  // 给子组件传递上传地址
  importData.data.upLoadUrl = goodsImportData()
  //打开导入弹窗
  importData.data.importShow = true
}

  /**
   * @method 导入关闭
   */
const importClosed = (is: boolean) => {
  importData.data.importShow = is
  // 刷新列表数据
  setHttpTableData()
}

 /**
   * @method 下载模板
   */
const onDownload = () => {
   // 后端接口返回模板URL,前端直接下载excel
  goodsImportTemplate()
    .then((res) => {
      if (res.ResultCode === 200 && res.Success) {
        uploadFile(res.Tag)
      }
    })
    .catch((err) => {
      console.log(err)
    })
}

  /**
   * @method 上传成功后下载的失败/成功的文件
   */
const onDownloadFile = (url: string) => {
  // 子组件发射事件,传出文件URL,上传失败后下载文件
  uploadFile(url)
}

/**
 * @method 下载文件
 */
const uploadFile = (file: string) => {
  window.open(encodeURI(file), 'foo', 'noopener=yes,noreferrer=yes')
}

4.2: 父组件为每一个页面,但是后台管理中父组件有无数个页面,就意味着上述重复的代码要写无数次,那我们直接封装成hook调用即可

5.hook封装使用

5.1: 新建useImport.ts文件

vue3导入导出_第3张图片

5.2:代码演示

import { reactive, getCurrentInstance, type ComponentInternalInstance } from 'vue'
import { axiosResponse } from '@/type/interface'
type CallBackType = ((...args: any[]) => string) | string
export default function useImport() {
  const { proxy } = getCurrentInstance() as ComponentInternalInstance
  /**导入参数 */
  const importData = reactive({
    data: {
      importShow: false,
      upLoadUrl: '',
    },
  })

  /**
   * @method 打开导入弹窗
   * @param callBack 获取导入地址函数 / 导入地址
   */
  function handleImport(callBack: T extends CallBackType ? T : never) {
    importData.data.upLoadUrl = typeof callBack === 'function' ? callBack() : callBack
    importData.data.importShow = true
  }

  /**
   * @method 下载模板
   */
  async function onDownload(callBack: () => Promise) {
    try {
      const { Success, Tag, ResultCode } = await callBack()
      if (ResultCode === 200 && Success) {
        proxy?.$_u.uploadFile(Tag)
      }
    } catch (error) {
      console.log('下载模板error', error)
    }
  }

  /**
   * @method 导入弹窗关闭事件
   * @param is 是否关闭
   * @param callBack 关闭后回调(一般为重新请求)
   */
  function importClosed(is: boolean, callBack: (...args: any[]) => void) {
    importData.data.importShow = is
    callBack()
  }

  return {
    importData,
    importClosed,
    onDownload,
    handleImport,
  }
}

5.3:父组件使用

  导入




import useImport from '@/hooks/useImport'

const { importData, importClosed, onDownload, handleImport } = useImport()

/**
 * 操作按钮
 * @param type 操作类型 add:新增 on:启用 off:禁用 import:导入 export:导出 print:打印
 */
const handleOpera = (type: operaType) => {
  switch (type) {
    case 'add':
      router.push({ name: 'workSpaceAdd' })
      break
    case 'on':
    case 'off':
      handleEnable(type)
      break
    case 'import':
      handleImport(proxy!.$api.workSpaceList_api.workAreaImport)
      break
    case 'export':
      handleExport('工作区', workAreaExportWorkArea, queryInfo)
      break

    default:
      break
  }
}

6.导出hooks封装

6.1:导出功能较为简单,一般是根据条件筛选导出,前端只需要把条件传给后端以及调用导出接口,后端返回URL后前端下载excel即可

6.2:导出hook代码演示



import { getCurrentInstance, type ComponentInternalInstance } from 'vue'
import { axiosResponse } from '@/type/interface'
export default function useExport() {
  const { proxy } = getCurrentInstance() as ComponentInternalInstance

  /**
   * @method 导出
   * @param from 单据来源
   * @param callBack 请求回调
   * @param exportInfo 导出参数
   */
  async function handleExport(from: string, callBack: (exportInfo: Record) => Promise, exportInfo: Record) {
    try {
      const { Success, Tag, ResultCode } = await callBack(exportInfo)
      if (ResultCode === 200 && Success) {
        Tag ? proxy!.$_u.uploadFile(Tag) : proxy!.$message.error(`暂无${from}信息导出数据`)
      }
    } catch (error) {
      console.log(`${from}导出error`, error)
    }
  }

  return {
    handleExport,
  }
}

6.2:代码演示

 导出

import useExport from '@/hooks/useExport'
const { handleExport } = useExport()

const queryInfo = {
  code: '',
  name: '',
  state: '',
  warehouseId: getTopMenu.value ? '' : activeWareHouse.value.warehouseId,
  warehouseRegionId: '',
  warehouseCodeOrName: '',
  workCodeOrName: '',
}

/**
 * 操作按钮
 * @param type 操作类型 add:新增 on:启用 off:禁用 import:导入 export:导出 print:打印
 */
const handleOpera = (type: operaType) => {
  switch (type) {
    case 'add':
      router.push({ name: 'workSpaceAdd' })
      break
    case 'on':
    case 'off':
      handleEnable(type)
      break
    case 'import':
      handleImport(proxy!.$api.workSpaceList_api.workAreaImport)
      break
    case 'export':
      handleExport('工作区', workAreaExportWorkArea, queryInfo)
      break

    default:
      break
  }
}

你可能感兴趣的:(TypeScript,vue3,hooks,vue.js,typescript)