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

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

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

使用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之类的提示语

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'

:importData="importData"

@closeImport="importClosed"

@download="onDownload"

@downloadFile="onDownloadFile"

>

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文件

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:父组件使用

导入

:importData="importData"

@closeImport="(is:boolean)=>importClosed(is,setHttpTableData)"

@download="onDownload(workAreaImportTemplate)"

@downloadFile="(url:string)=>proxy?.$_u.uploadFile(url)"

>

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

}

}

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: