feat:岗位 post 的实现 100%(export 功能)
							parent
							
								
									cac69a0283
								
							
						
					
					
						commit
						a08d73b034
					
				|  | @ -48,5 +48,7 @@ export function deletePost(id: number) { | ||||||
| 
 | 
 | ||||||
| /** 导出岗位 */ | /** 导出岗位 */ | ||||||
| export function exportPost(params: any) { | export function exportPost(params: any) { | ||||||
|   return requestClient.download('/system/post/export', params); |   return requestClient.download('/system/post/export', { | ||||||
|  |     params | ||||||
|  |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | // TODO @芋艿:需要优化下
 | ||||||
|  | // TODO @芋艿:是不是可以共用么?
 | ||||||
|  | /** | ||||||
|  |  * @description: base64 to blob | ||||||
|  |  */ | ||||||
|  | export function dataURLtoBlob(base64Buf: string): Blob { | ||||||
|  |   const arr = base64Buf.split(','); | ||||||
|  |   const typeItem = arr[0]; | ||||||
|  |   // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 | ||||||
|  |   const mime = typeItem!.match(/:(.*?);/)![1]; | ||||||
|  |   // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 | ||||||
|  |   const bstr = window.atob(arr[1]!); | ||||||
|  |   let n = bstr.length; | ||||||
|  |   const u8arr = new Uint8Array(n); | ||||||
|  |   while (n--) { | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 | ||||||
|  |     u8arr[n] = bstr.codePointAt(n)!; | ||||||
|  |   } | ||||||
|  |   return new Blob([u8arr], { type: mime }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * img url to base64 | ||||||
|  |  * @param url | ||||||
|  |  */ | ||||||
|  | export function urlToBase64(url: string, mineType?: string): Promise<string> { | ||||||
|  |   return new Promise((resolve, reject) => { | ||||||
|  |     let canvas = document.createElement('CANVAS') as HTMLCanvasElement | null; | ||||||
|  |     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 | ||||||
|  |     const ctx = canvas!.getContext('2d'); | ||||||
|  | 
 | ||||||
|  |     const img = new Image(); | ||||||
|  |     img.crossOrigin = ''; | ||||||
|  |     img.addEventListener('load', () => { | ||||||
|  |       if (!canvas || !ctx) { | ||||||
|  |         // eslint-disable-next-line prefer-promise-reject-errors
 | ||||||
|  |         return reject(); | ||||||
|  |       } | ||||||
|  |       canvas.height = img.height; | ||||||
|  |       canvas.width = img.width; | ||||||
|  |       ctx.drawImage(img, 0, 0); | ||||||
|  |       const dataURL = canvas.toDataURL(mineType || 'image/png'); | ||||||
|  |       canvas = null; | ||||||
|  |       resolve(dataURL); | ||||||
|  |     }); | ||||||
|  |     img.src = url; | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | @ -0,0 +1,126 @@ | ||||||
|  | // TODO @芋艿:需要优化下每个方法
 | ||||||
|  | // TODO @芋艿:是不是可以共用么?
 | ||||||
|  | import { dataURLtoBlob, urlToBase64 } from './base64Conver'; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Download online pictures | ||||||
|  |  * @param url | ||||||
|  |  * @param filename | ||||||
|  |  * @param mime | ||||||
|  |  * @param bom | ||||||
|  |  */ | ||||||
|  | export function downloadByOnlineUrl( | ||||||
|  |   url: string, | ||||||
|  |   filename: string, | ||||||
|  |   mime?: string, | ||||||
|  |   bom?: BlobPart, | ||||||
|  | ) { | ||||||
|  |   urlToBase64(url).then((base64) => { | ||||||
|  |     downloadByBase64(base64, filename, mime, bom); | ||||||
|  |   }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Download pictures based on base64 | ||||||
|  |  * @param buf | ||||||
|  |  * @param filename | ||||||
|  |  * @param mime | ||||||
|  |  * @param bom | ||||||
|  |  */ | ||||||
|  | export function downloadByBase64( | ||||||
|  |   buf: string, | ||||||
|  |   filename: string, | ||||||
|  |   mime?: string, | ||||||
|  |   bom?: BlobPart, | ||||||
|  | ) { | ||||||
|  |   const base64Buf = dataURLtoBlob(buf); | ||||||
|  |   downloadByData(base64Buf, filename, mime, bom); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Download according to the background interface file stream | ||||||
|  |  * @param {*} data | ||||||
|  |  * @param {*} filename | ||||||
|  |  * @param {*} mime | ||||||
|  |  * @param {*} bom | ||||||
|  |  */ | ||||||
|  | export function downloadByData( | ||||||
|  |   data: BlobPart, | ||||||
|  |   filename: string, | ||||||
|  |   mime?: string, | ||||||
|  |   bom?: BlobPart, | ||||||
|  | ) { | ||||||
|  |   const blobData = bom === undefined ? [data] : [bom, data]; | ||||||
|  |   const blob = new Blob(blobData, { type: mime || 'application/octet-stream' }); | ||||||
|  | 
 | ||||||
|  |   const blobURL = window.URL.createObjectURL(blob); | ||||||
|  |   const tempLink = document.createElement('a'); | ||||||
|  |   tempLink.style.display = 'none'; | ||||||
|  |   tempLink.href = blobURL; | ||||||
|  |   tempLink.setAttribute('download', filename); | ||||||
|  |   if (tempLink.download === undefined) | ||||||
|  |     tempLink.setAttribute('target', '_blank'); | ||||||
|  | 
 | ||||||
|  |   document.body.append(tempLink); | ||||||
|  |   tempLink.click(); | ||||||
|  |   tempLink.remove(); | ||||||
|  |   window.URL.revokeObjectURL(blobURL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Download file according to file address | ||||||
|  |  * @param {*} sUrl | ||||||
|  |  */ | ||||||
|  | export function downloadByUrl({ | ||||||
|  |   url, | ||||||
|  |   target = '_blank', | ||||||
|  |   fileName, | ||||||
|  | }: { | ||||||
|  |   fileName?: string; | ||||||
|  |   target?: '_blank' | '_self'; | ||||||
|  |   url: string; | ||||||
|  | }): boolean { | ||||||
|  |   const isChrome = window.navigator.userAgent.toLowerCase().includes('chrome'); | ||||||
|  |   const isSafari = window.navigator.userAgent.toLowerCase().includes('safari'); | ||||||
|  | 
 | ||||||
|  |   if (/iP/.test(window.navigator.userAgent)) { | ||||||
|  |     console.error('Your browser does not support download!'); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   if (isChrome || isSafari) { | ||||||
|  |     const link = document.createElement('a'); | ||||||
|  |     link.href = url; | ||||||
|  |     link.target = target; | ||||||
|  | 
 | ||||||
|  |     if (link.download !== undefined) | ||||||
|  |       link.download = fileName || url.slice(url.lastIndexOf('/') + 1); | ||||||
|  | 
 | ||||||
|  |     if (document.createEvent) { | ||||||
|  |       const e = document.createEvent('MouseEvents'); | ||||||
|  |       e.initEvent('click', true, true); | ||||||
|  |       link.dispatchEvent(e); | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!url.includes('?')) url += '?download'; | ||||||
|  | 
 | ||||||
|  |   openWindow(url, { target }); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function openWindow( | ||||||
|  |   url: string, | ||||||
|  |   opt?: { | ||||||
|  |     noopener?: boolean; | ||||||
|  |     noreferrer?: boolean; | ||||||
|  |     target?: '_blank' | '_self' | string; | ||||||
|  |   }, | ||||||
|  | ) { | ||||||
|  |   const { noopener = true, noreferrer = true, target = '__blank' } = opt || {}; | ||||||
|  |   const feature: string[] = []; | ||||||
|  | 
 | ||||||
|  |   noopener && feature.push('noopener=yes'); | ||||||
|  |   noreferrer && feature.push('noreferrer=yes'); | ||||||
|  | 
 | ||||||
|  |   window.open(url, target, feature.join(',')); | ||||||
|  | } | ||||||
|  | @ -11,10 +11,11 @@ import { getPostPage, deletePost, exportPost } from '#/api/system/post'; | ||||||
| 
 | 
 | ||||||
| import { Page, useVbenModal } from '@vben/common-ui'; | import { Page, useVbenModal } from '@vben/common-ui'; | ||||||
| import { Button, message } from 'ant-design-vue'; | import { Button, message } from 'ant-design-vue'; | ||||||
| import { Plus } from '@vben/icons'; | import { Plus, Download } from '@vben/icons'; | ||||||
| 
 | 
 | ||||||
| import { useGridColumns, useGridFormSchema } from './data'; | import { useGridColumns, useGridFormSchema } from './data'; | ||||||
| import Form from './modules/form.vue'; | import Form from './modules/form.vue'; | ||||||
|  | import {downloadByData} from '#/utils/download'; | ||||||
| 
 | 
 | ||||||
| const [FormModal, formModalApi] = useVbenModal({ | const [FormModal, formModalApi] = useVbenModal({ | ||||||
|   connectedComponent: Form, |   connectedComponent: Form, | ||||||
|  | @ -90,7 +91,6 @@ const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
|       keyField: 'id', |       keyField: 'id', | ||||||
|     }, |     }, | ||||||
|     toolbarConfig: { |     toolbarConfig: { | ||||||
|       export: true, // TODO @芋艿:导出 |  | ||||||
|       refresh: { code: 'query' }, |       refresh: { code: 'query' }, | ||||||
|       search: true, |       search: true, | ||||||
|     }, |     }, | ||||||
|  | @ -101,6 +101,12 @@ const [Grid, gridApi] = useVbenVxeGrid({ | ||||||
| function refreshGrid() { | function refreshGrid() { | ||||||
|   gridApi.query(); |   gridApi.query(); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** 导出表格 */ | ||||||
|  | async function onExport() { | ||||||
|  |   const data = await exportPost(await gridApi.formApi.getValues()); | ||||||
|  |   downloadByData(data, '导出岗位.xls'); | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|   <Page auto-content-height> |   <Page auto-content-height> | ||||||
|  | @ -111,6 +117,10 @@ function refreshGrid() { | ||||||
|           <Plus class="size-5" /> |           <Plus class="size-5" /> | ||||||
|           {{ $t('ui.actionTitle.create', ['岗位']) }} |           {{ $t('ui.actionTitle.create', ['岗位']) }} | ||||||
|         </Button> |         </Button> | ||||||
|  |         <Button type="primary" class="ml-2" @click="onExport"> | ||||||
|  |           <Download class="size-5" /> | ||||||
|  |           {{ $t('ui.actionTitle.export') }} | ||||||
|  |         </Button> | ||||||
|       </template> |       </template> | ||||||
|     </Grid> |     </Grid> | ||||||
|   </Page> |   </Page> | ||||||
|  |  | ||||||
|  | @ -63,4 +63,5 @@ export { | ||||||
|   SwatchBook, |   SwatchBook, | ||||||
|   UserRoundPen, |   UserRoundPen, | ||||||
|   X, |   X, | ||||||
|  |   Download, | ||||||
| } from 'lucide-vue-next'; | } from 'lucide-vue-next'; | ||||||
|  |  | ||||||
|  | @ -13,7 +13,8 @@ | ||||||
|     "edit": "Modify {0}", |     "edit": "Modify {0}", | ||||||
|     "create": "Create {0}", |     "create": "Create {0}", | ||||||
|     "delete": "Delete {0}", |     "delete": "Delete {0}", | ||||||
|     "view": "View {0}" |     "view": "View {0}", | ||||||
|  |     "export": "Export" | ||||||
|   }, |   }, | ||||||
|   "actionMessage": { |   "actionMessage": { | ||||||
|     "deleteConfirm": "Are you sure to delete {0}?", |     "deleteConfirm": "Are you sure to delete {0}?", | ||||||
|  |  | ||||||
|  | @ -13,7 +13,8 @@ | ||||||
|     "edit": "修改{0}", |     "edit": "修改{0}", | ||||||
|     "create": "新增{0}", |     "create": "新增{0}", | ||||||
|     "delete": "删除{0}", |     "delete": "删除{0}", | ||||||
|     "view": "查看{0}" |     "view": "查看{0}", | ||||||
|  |     "export": "导出" | ||||||
|   }, |   }, | ||||||
|   "actionMessage": { |   "actionMessage": { | ||||||
|     "deleteConfirm": "确定删除 {0} 吗?", |     "deleteConfirm": "确定删除 {0} 吗?", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 YunaiV
						YunaiV