import getMD5 from '@/components/MD5'; import { useAjax } from '@/Hook/useAjax'; import { bdSysMediaListImg, bdSysMediaListVideo, bdSysMediaAdd, delMedia, bdSysMediaEdit, getFileUrl, getMedia, configSortApi, getFolderTreeImg, getFolderTreeVideo, editMediaFolder } 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弹窗 newsVisrible?: boolean,//news弹窗 knewsVisrible?: boolean,//客服图文弹窗 fileName?: string,//文件夹名称 sort?: number, // 排序 videoTitle?: string,//video文件名称 videoDescription?: string,//视频描述 belongUser?: any,//0公共本地|1个人本地 mediaType?: 'VIDEO' | 'IMG',//类型 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,//排序弹窗 size?: number, // 需要上传素材的大小 upLoadLoading?: boolean, // 上传loading } 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, fileName: '', videoTitle: '', videoDescription: '', belongUser: '1', mediaType: 'IMG', parentId: null, selectFile: [], delPupId: '', rightClickPup: { id: '' }, publicPath: [{ title: '公共本地', number: '0' }], path: [{ title: '个人本地', number: '0' }], imgVisrible: false, newsVisrible: false, sortVisible: false, sort: 0, } /**本地素材管理器 */ function useBdMediaPup() { const [state, dispatch]: [State, Dispatch] = useReducer(reducer, initData) const { fileName, sort, belongUser, mediaType, parentId, selectFile, delPupId, rightClickPup, actionItem, path, publicPath, size, videoTitle, videoDescription } = state const listImg = useAjax((params) => bdSysMediaListImg(params)) const listVideo = useAjax((params) => bdSysMediaListVideo(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 get_folder_tree_img = useAjax((params: any) => getFolderTreeImg(params)) const get_folder_tree_video = useAjax((params: any) => getFolderTreeVideo(params)) const edit_media_folder = useAjax((params: any) => editMediaFolder(params)) const get = useAjax((params) => getMedia(params))//获取本地素材详情 const [isOk, setIsOk] = useState(true) //请求上传地址 const fileUrl = useAjax((params: { type: string, fileType: 'video' | 'image' }) => getFileUrl(params), { manual: true, }) /**初始化数据 */ const init = useCallback((params: any) => { console.log('init===>', params) dispatch({ type: 'init', params }) }, []) /**设置state */ const set = useCallback((params: State) => { // console.log('params===>',params) 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) => { if (mediaType === 'IMG') { get_folder_tree_img.refresh() } else if (mediaType === 'VIDEO') { get_folder_tree_video.refresh() } if (mediaType === 'IMG') { listImg.refresh() } else if (mediaType === 'VIDEO') { listVideo.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 } add.run(obj).then((res) => { console.log(res) if (mediaType === 'IMG') { listImg.refresh() } else if (mediaType === 'VIDEO') { listVideo.refresh() } offEditFile()//关闭弹窗并清空相关数据 set({ upLoadLoading: false }) }).catch(() => set({ upLoadLoading: false })) } } } else { message.error('上传失败!') } }).catch(() => 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) => { if (mediaType === 'IMG') { listImg.refresh() } else if (mediaType === 'VIDEO') { listVideo.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) { if (mediaType === 'IMG') { listImg.refresh() get_folder_tree_img.refresh() } else if (mediaType === 'VIDEO') { listVideo.refresh() get_folder_tree_video.refresh() } } }) }) } }, [listImg, listVideo, selectFile, mediaType]) /**获取本地素材数据列表 */ const getList = useCallback((props?: any) => { let obj = { pageSize: 20, pageNum: 1, belongUser: belongUser === '0' ? false : true, parentId, ...props } if (mediaType === 'IMG') { listImg.run(obj).then((res) => { setIsOk(true) }) } else if (mediaType === 'VIDEO') { listVideo.run(obj).then((res) => { setIsOk(true) }) } }, [mediaType, belongUser, parentId]) /**选中文件 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) console.log(item, belongUser == '1') 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(item?.id === 0 ? { path: [path[0]] } : { path: [path[0], item] }) } if (belongUser == '0' && publicPath) { set(item?.id === 0 ? { publicPath: [publicPath[0]] } : { 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 }) console.log(item) getList({ pageSize: 20, pageNum: 1, mediaType, belongUser: belongUser === '0' ? false : true, parentId: item.id }) console.log('文件夹被点击') }, [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 }, isAll?: boolean) => { let { id } = item e?.stopPropagation()//阻止冒泡传递到文件夹被点击事件 let state = selectFile?.some((i) => i === id) if (isAll) { if (state) {//存在就是删除 set({ selectFile: selectFile?.filter(i => i !== id) }) } else {//否则新增 set({ selectFile: [...selectFile as number[], id] }) } } else {//单选情况存在于选择素材弹窗组件 if (item?.folder && mediaType !== 'IMG') {//假如是文件不让选择 message.error('不能选择文件夹') return } if (state) {//存在就是删除 set({ selectFile: selectFile?.filter(i => i !== id) }) set({ selectItem: null }) } else {//否则新增 set({ selectFile: [id] }) set({ selectItem: item }) } } }, [selectFile, mediaType]) /**开启删除弹窗 */ 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) // if (rightClickPup?.mediaType !== 'news') {//不是图文开启编辑名字弹窗 let obj = { fileVisible: true, actionItem: rightClickPup, 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, sortVisible: false, actionItem: '', fileName: '', selectFile: selectFile?.filter(id => id !== actionItem?.id), sort: 0 }) }, [selectFile, actionItem]) /**全选反选文件*/ const allFile = useCallback(() => { let allArr: any[] = [] if (mediaType === 'IMG') { listImg?.data?.records?.forEach((item: { id: any }) => { allArr.push(item.id) }) } else if (mediaType === "VIDEO") { listVideo?.data?.records?.forEach((item: { id: any }) => { allArr.push(item.id) }) } set({ selectFile: allArr.filter((i) => selectFile?.every(id => id !== i)) }) }, [selectFile, listImg, listVideo, mediaType]) return { state, init, set, addFile, addFolder, getList, dels, onFile, changeClickFile, allFile, delPupOn, delPupOff, editFile, offEditFile, nameOk, fileClick, treeClick, pathClick, configSort, fileUrl, listImg, listVideo, add, get, edit, typeEnum, get_folder_tree_img, get_folder_tree_video, edit_media_folder } } export default useBdMediaPup