import getMD5 from '@/components/MD5'; import { useAjax } from '@/Hook/useAjax'; import { bdSysMediaList, bdSysMediaAdd, delMedia, bdSysMediaEdit, getFileUrl, getMedia, configSortApi, getFolderTree, editMediaFolder, bdSysMediaAddsApi, } from '@/services/launchAdq/material'; import { blobToBase64, dataURLtoFile, videoMessage } from '@/utils/compress'; import { getImgSize } from '@/utils/utils'; import { message } from 'antd'; import { compressAccurately } from 'image-conversion'; import { Dispatch, useCallback, useReducer, useState } from 'react'; import { request } from 'umi'; /**新本地素材弹窗 */ type State = { fileVisible?: boolean; //文件夹弹窗 imgVisrible?: boolean; //img,voice,video弹窗 imgsVisrible?: boolean,// newsVisrible?: boolean; //news弹窗 knewsVisrible?: boolean; //客服图文弹窗 fileName?: string; //文件夹名称 sort?: number; // 排序 videoTitle?: string; //video文件名称 videoDescription?: string; //视频描述 belongUser?: any; //0公共本地|1个人本地 mediaType?: 'VIDEO' | 'IMG' | 'PAGE'; // 类型 parentId?: any; //上级目录ID,顶级使用null url?: string; //素材地址 pathId?: string; //路径ID selectFile?: number[]; //选中的文件列表 rightClickPup?: any; // /**右键菜单开关 all为全部 id 为目标文件 空为不显示*/ xy?: { x: number; y: number }; // /**鼠标位置 菜单弹窗位置使用*/ delPupId?: any; //按了单个删除存放的id为了让弹窗区分文件 actionItem?: any; //当前选中的Itme path?: any[]; //个人路径 publicPath?: any[]; //本地路径 file?: File; //素材文件 selectItem?: any[]; //单选素材时存放选中的素材 knewsdefaultData?: any; //k图文编辑时的默认内容 sortVisible?: boolean; //排序弹窗 num?: number; //选择数量 size?: number; // 需要上传素材的大小 upLoadLoading?: boolean; cloudSize?: any[]; maxSize?: number; //素材最大尺寸 adcreativeTemplateId?: number; //创意模板 id promotedObjectType?: string; //推广目标类型 promotedObjectId?: string; //推广目标 id pageType?: string; //落地页类型 canvasType?: string; //原生页类型 siteSet?: string[]; //版位 sourceType?: string; //视频/图片/原生页等素材的创建来源类型 marketingScene?: string; //营销场景 }; export type Action = { type: 'set' | 'init'; params?: any; }; const typeEnum = { IMG: '图片', VIDEO: '视频', }; function reducer(state: State, action: Action) { let { type, params } = action; let newState = JSON.parse(JSON.stringify(state)); newState.file = state.file; switch (type) { case 'set': Object.keys(params as State).forEach((key: string) => { newState[key] = (params as State)[key]; }); return newState; case 'init': return { ...initData, ...params }; default: return state; } } const initData: State = { fileVisible: false, knewsVisrible: false, videoTitle: '', videoDescription: '', fileName: '', belongUser: '1', mediaType: 'IMG', parentId: null, selectFile: [], delPupId: '', rightClickPup: { id: '' }, publicPath: [{ title: '公共本地', number: '0' }], path: [{ title: '个人本地', number: '0' }], imgVisrible: false, imgsVisrible: false, newsVisrible: false, sortVisible: false, sort: 0, num: 1, }; /**本地素材管理器 */ function useBdMediaPup() { const [state, dispatch]: [State, Dispatch] = useReducer(reducer, initData); const { fileName, sort, belongUser, mediaType, parentId, selectFile, selectItem, delPupId, cloudSize, num, rightClickPup, actionItem, path, publicPath, videoTitle, videoDescription, size, maxSize, adcreativeTemplateId, promotedObjectType, promotedObjectId, pageType, canvasType, siteSet, sourceType, marketingScene, } = state; const list = useAjax((params) => bdSysMediaList(params)); const add = useAjax((params) => bdSysMediaAdd(params), { msgNmae: '新增' }); const del = useAjax((params) => delMedia(params), { msgNmae: '删除' }); const edit = useAjax((params) => bdSysMediaEdit(params), { msgNmae: '编辑' }); const configSort = useAjax((params) => configSortApi(params), { msgNmae: '排序' }); const adds = useAjax((params) => bdSysMediaAddsApi(params), { msgNmae: '新增' }) const get = useAjax((params) => getMedia(params)); //获取图文详情 const get_folder_tree = useAjax((params: any) => getFolderTree(params)); const edit_media_folder = useAjax((params: any) => editMediaFolder(params)); const [isOk, setIsOk] = useState(true); //请求上传地址 const fileUrl = useAjax( (params: { type: string; fileType: 'video' | 'image' }) => getFileUrl(params), { manual: true, }, ); /**初始化数据 */ const init = useCallback((params: State) => { console.log('params======>', params); dispatch({ type: 'init', params }); }, []); /**设置state */ const set = useCallback((params: State) => { if (params) { dispatch({ type: 'set', params }); } }, []); /**新增文件夹 */ const addFolder = () => { if (fileName) { let obj = { title: fileName, mediaType, folder: true, parentId, belongUser: belongUser === '0' ? false : true, sort, }; add.run(obj).then((res) => { get_folder_tree.refresh(); list.refresh(); offEditFile(); //关闭弹窗并清空相关数据 }); } }; /** 新增图片 视频 */ const addFile = async (data: any) => { if (data) { //存在代表素材 if (!data) { return; } if (data?.file) { let file = data.file; let fileSize = size || 0; if (!size) { if (mediaType === 'IMG') { fileSize = 307200; } else { fileSize = 104857600; } } if (mediaType === 'IMG') { if (file?.size > fileSize) { // 大于300kb进入压缩 let bole = await compressAccurately(file, 250); if (bole?.size > 300000) { bole = await compressAccurately(file, 200); } if (bole?.size > 300000) { bole = await compressAccurately(file, 150); } if (bole?.size > 300000) { bole = await compressAccurately(file, 100); } let newFile = await blobToBase64(bole); message.warning({ content: `选择的图片大于${fileSize / 1024}KB,图片已压缩`, duration: 3, }); file = await dataURLtoFile(newFile, file?.name); } } else if (mediaType === 'VIDEO') { if (file?.size > fileSize) { // 大于100mb进入压缩 message.error({ content: `选择的视频大于${fileSize / 1024 / 1024}MB,请重新选择提交`, duration: 3, }); return; } } set({ upLoadLoading: true }); let width = 0; let height = 0; if (mediaType === 'IMG') { let imgData = await getImgSize(file); width = imgData.width; height = imgData.height; } else if (mediaType === 'VIDEO') { let videoInfo: any = await videoMessage([file]); width = videoInfo[0].width; height = videoInfo[0].height; } /**修改文件名以用户设置的文件title命名*/ let newFile = new File( [file], data?.title ? data?.title + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type }, ); let formData = new FormData(); /**向阿里云请求上传地址*/ fileUrl .run({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }) .then((res1) => { Object.keys(res1).forEach((key: string) => { if (key !== 'url') { formData.append(key, res1[key]); } }); formData.append('file', newFile); /**向阿里云返回的上传地址上传文件*/ request(res1?.ossUrl, { method: 'post', body: formData }) .then(async (res2: { code: number; data: { url: string } }) => { if (res2.code === 200) { message.success('上传成功'); if (data) { /**取到返回的文件地址向后端发送具体数据*/ if (res2?.data?.url) { let fileMd5 = await getMD5(newFile); let obj = { title: data.title, mediaType, folder: false, parentId, width, height, fileMd5, belongUser: belongUser === '0' ? false : true, url: res2?.data?.url, sort: data?.sort, fileSize: newFile?.size, fileMime: newFile.type, }; if (mediaType === 'VIDEO') { obj['videoTitle'] = data?.videoTitle || data?.title; obj['videoDescription'] = data?.videoDescription; } if (!obj?.title) { obj.title = data.file.name } if (obj.title.match(RegExp(/[<>&\\'"/\x08\x09\x0A\x0D\x7F]/g))) { obj.title = obj.title.replace(RegExp(/[<>&\\'"/\x08\x09\x0A\x0D\x7F]/g), '') } obj.title = obj.title.replace(/\.(jpg|jpeg|gif|png)$/i, '') add.run(obj).then((res) => { list.refresh(); offEditFile(); //关闭弹窗并清空相关数据 set({ upLoadLoading: false }); }).catch(() => set({ upLoadLoading: false })); } } } else { message.error('上传失败!'); } }) .catch(() => set({ upLoadLoading: false })); }) .catch(() => set({ upLoadLoading: false })); } } }; /** 批量新增图片 视频 */ const addFiles = async (data: any) => { if (data) {//存在代表素材 if (!data) { return } let { files, ...value } = data let params = files?.map(async (item: any, index: number) => { let file = item let fileSize = size || 0 if (mediaType === 'IMG') { fileSize = value?.fileSize ? value?.fileSize * 1024 : 307200 } else { fileSize = 104857600 } if (mediaType === 'IMG') { let bad = 50 let size = fileSize / 1024 if (size > 10 && size <= 50) { bad = 5 } else if (size > 50 && size <= 100) { bad = 10 } else if (size > 100 && size <= 300) { bad = 20 } else { bad = 50 } if (file?.size > fileSize) { // 大于300kb进入压缩 let bole = await compressAccurately(file, fileSize / 1024 - bad) if (bole?.size > fileSize) { bole = await compressAccurately(file, fileSize / 1024 - bad * 2) } if (bole?.size > fileSize) { bole = await compressAccurately(file, fileSize / 1024 - bad * 3) } if (bole?.size > fileSize) { bole = await compressAccurately(file, fileSize / 1024 - bad * 4) } let newFile = await blobToBase64(bole) message.warning({ content: `选择的图片大于${fileSize / 1024}KB,图片已压缩`, duration: 3 }) file = await dataURLtoFile(newFile, file?.name) } } else if (mediaType === 'VIDEO') { if (file?.size > fileSize) { // 大于100mb进入压缩 message.error({ content: `选择的视频大于${fileSize / 1024 / 1024}MB,请重新选择提交`, duration: 3 }) return } } let width = 0 let height = 0 if (mediaType === 'IMG') { let imgData = await getImgSize(file) width = imgData.width height = imgData.height } else if (mediaType === "VIDEO") { let videoInfo: any = await videoMessage([file]) width = videoInfo[0].width height = videoInfo[0].height } /**修改文件名以用户设置的文件title命名*/ let newFile = new File([file], data?.title ? data?.title + index + 1 + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type }) let formData = new FormData(); set({ upLoadLoading: true }) let res = await getFileUrl({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }).catch(() => set({ upLoadLoading: false })) let res1 = res.data Object.keys(res1).forEach((key: string) => { if (key !== 'url') { formData.append(key, res1[key]) } }) formData.append('file', newFile) /**向阿里云返回的上传地址上传文件*/ let data1: { code: number, data: { url: string } } = await request(res1?.ossUrl, { method: 'post', body: formData }).catch(() => set({ upLoadLoading: false })) message.success('上传成功') let fileMd5 = await getMD5(newFile) let obj = { title: data.title + (index + 1).toString(), folder: false, parentId, width, height, fileMd5, url: data1?.data?.url, sort: data?.sort, fileSize: newFile?.size, fileMime: newFile.type } if (mediaType === 'VIDEO') { obj['videoTitle'] = data?.videoTitle || data?.title obj['videoDescription'] = data?.videoDescription } return { ...obj } }) Promise.all(params).then(res => { adds.run({ belongUser: belongUser === '0' ? false : true, data: res }).then(response => { list.refresh() offEditFile()//关闭弹窗并清空相关数据 set({ upLoadLoading: false }) }).catch(() => set({ upLoadLoading: false })) }) } } /**编辑非图文素材名称*/ const nameOk = useCallback((selectWx: any) => { if (fileName && actionItem) { let obj = { title: fileName, belongUser: belongUser === '0' ? false : true, sysMediaId: actionItem?.id, mediaType: actionItem?.mediaType, folder: actionItem?.folder, url: actionItem?.url, sort, }; // if (mediaType === 'VIDEO') { // obj['videoTitle'] = videoTitle; // obj['videoDescription'] = videoDescription; // } edit.run(obj).then((res) => { list.refresh(); get_folder_tree.refresh() offEditFile(); //关闭弹窗并清空相关数据 }); } }, [fileName, actionItem, edit, belongUser, mediaType, videoTitle, videoDescription], ); /**删除文件 */ const dels = useCallback((id?: any) => { let arr = typeof id === 'number' ? [id] : selectFile; let len = arr?.length || 0; if (len) { arr?.map((id, index) => { del.run({ sysMediaId: id, mediaType }).then(() => { set({ selectFile: selectFile?.filter((i) => i !== id) }); //清理已删除文件 if (index === len - 1) { list.refresh(); get_folder_tree.refresh(); } }); }); } else { message.error('请选择文件') } }, [list, selectFile, mediaType], ); /**获取本地素材数据列表 */ const getList = useCallback( (props?: any) => { console.log('getList====>', props); let obj = { pageSize: 30, pageNum: 1, belongUser: belongUser === '0' ? false : true, mediaType, parentId, sizeQueries: cloudSize, maxSize, ...props, }; if (mediaType === 'PAGE') { obj = { ...obj, adcreativeTemplateId, promotedObjectType, promotedObjectId, pageType, canvasType, siteSet, sourceType, marketingScene, }; } list.run(obj).then((res) => { setIsOk(true); }); }, [list, mediaType, belongUser, parentId, cloudSize, maxSize, adcreativeTemplateId, promotedObjectType, promotedObjectId, pageType, canvasType, siteSet, sourceType, marketingScene], ); /**选中文件 single 开启可单选为了在右键删除选择时只选一个*/ const onFile = useCallback( (e: any, item: { id: any; folder?: boolean }, isAll?: boolean, single?: boolean) => { let { id } = item; e?.stopPropagation(); if (isAll && !single) { set({ selectFile: [...new Set([...(selectFile as number[]), id])] }); } else { if (item?.folder && !single) { //假如是文件不让选择 message.error('不能选择文件夹'); return; } set({ selectFile: [item.id], selectItem: [item] }); } }, [selectFile], ); /**点击文件夹 */ const fileClick = useCallback( (item) => { if (isOk) { setIsOk(false); if (belongUser == '1' && path) { set({ path: [...path, item] }); } if (belongUser == '0' && publicPath) { set({ publicPath: [...publicPath, item] }); } set({ parentId: item.id }); getList({ parentId: item.id }); //请求对应文件夹列表 } }, [path, publicPath, mediaType, belongUser, isOk], ); /**点击目录树*/ const treeClick = useCallback( (item) => { if (isOk) { setIsOk(false); console.log(item, belongUser == '1'); if (belongUser == '1' && path) { set({ path: [path[0], item] }); } if (belongUser == '0' && publicPath) { set({ publicPath: [publicPath[0], item] }); } set({ parentId: item.id }); getList({ parentId: item.id }); //请求对应文件夹列表 } }, [path, publicPath, mediaType, belongUser, isOk], ); /**点击路径 */ const pathClick = useCallback( (item) => { let newPath: any[] = []; if (belongUser == '1') { path?.forEach((paths, index) => { //用传入的index和path循环的index对比小于等于index代表该保留的路径 if (index <= item.index) { newPath.push(paths); } }); set({ path: newPath }); } else { publicPath?.forEach((paths, index) => { //用传入的index和path循环的index对比小于等于index代表该保留的路径 if (index <= item.index) { newPath.push(paths); } }); set({ publicPath: newPath }); } set({ parentId: item.parentId }); getList({ pageSize: 30, pageNum: 1, mediaType, belongUser: belongUser === '0' ? false : true, parentId: item.id, }); }, [path, mediaType, belongUser, publicPath], ); /**取消文件 */ const offFile = useCallback( (e: any, item: { id: any }) => { let { id } = item; set({ selectFile: selectFile?.filter((i) => i !== id) }); }, [selectFile], ); /**判断是取消还是选中文件的操作在点击打钩时使用 */ const changeClickFile = useCallback((e: any, item: { id: any; folder?: boolean, url: string }, isAll?: boolean, noFile?: boolean) => { let { id, url } = item; e?.stopPropagation(); //阻止冒泡传递到文件夹被点击事件 let state = selectItem?.some((i) => i.url === url); if (state) { //存在就是删除 set({ selectFile: selectFile?.filter((i) => i !== id), selectItem: selectItem?.filter((i: { url: string }) => i.url !== url), }); } else { //否则新增 if (num === 1) { if (state) { //存在就是删除 set({ selectFile: selectFile?.filter((i) => i !== id), selectItem: [] }); } else { //否则新增 set({ selectFile: [id], selectItem: [item] }); } } else { if (selectFile && num && selectFile?.length >= num) { message.error(`只能选择${num}张`); return; } let newSelectItem = selectItem || []; newSelectItem.push(item); set({ selectItem: newSelectItem, selectFile: [...(selectFile as number[]), id] }); } } // } }, [selectFile, mediaType, num], ); /**开启删除弹窗 */ const delPupOn = useCallback((delPupId) => { set({ delPupId }); }, []); /**关闭删除弹窗并去除选中 */ const delPupOff = useCallback(() => { set({ delPupId: '' }); offFile(null, { id: delPupId }); }, [delPupId]); /**编辑 */ const editFile = useCallback((e?: any) => { e?.stopPropagation(); onFile(null, rightClickPup, true, true); let obj = { fileVisible: true, actionItem: { ...rightClickPup, fileType: mediaType }, fileName: rightClickPup.title, sort: rightClickPup.sort }; // if (rightClickPup?.mediaType === "video") { // obj['videoTitle'] = rightClickPup?.videoTitle || rightClickPup?.title; // obj['videoDescription'] = rightClickPup?.videoDescription; // } set(obj); }, [rightClickPup], ); /**取消编辑后清空选中存放的数据并关闭弹窗*/ const offEditFile = useCallback(() => { set({ fileVisible: false, imgVisrible: false, imgsVisrible: false, sortVisible: false, actionItem: '', fileName: '', selectFile: selectFile?.filter((id) => id !== actionItem?.id), sort: 0, }); }, [selectFile, actionItem]); /**全选反选文件*/ const allFile = useCallback(() => { let allArr: any[] = [], allFile: any[] = []; list?.data?.records?.forEach((item: { id: any }) => { allArr.push(item.id); allFile.push(item) }); set({ selectFile: allArr.filter((i) => selectFile?.every((id) => id !== i)), selectItem: allFile.filter((i) => selectItem?.every((item: any) => item.id !== i.id)) }); }, [selectFile, list, mediaType, selectItem]); return { state, init, set, addFile, addFolder, getList, dels, onFile, changeClickFile, allFile, delPupOn, delPupOff, editFile, offEditFile, nameOk, fileClick, treeClick, pathClick, addFiles, configSort, list, add, get, edit, typeEnum, get_folder_tree, edit_media_folder, }; } export default useBdMediaPup;