|
@@ -0,0 +1,432 @@
|
|
|
+import getMD5 from '@/components/MD5';
|
|
|
+import { useAjax } from '@/Hook/useAjax';
|
|
|
+import { bdSysMediaList, bdSysMediaAdd, delMedia, bdSysMediaEdit, getFileUrl, syncMedia, getMedia, configSortApi, syncForSend, wxSysMediaAdd, editWxlist, getFolderTree, editMediaFolder } from '@/services/launchAdq/material';
|
|
|
+import { message } from 'antd';
|
|
|
+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个人本地
|
|
|
+ fileType?: 'image' | 'news' | 'voice' | 'video' | 'knews',//类型
|
|
|
+ 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,//排序弹窗
|
|
|
+}
|
|
|
+export type Action = {
|
|
|
+ type: 'set' | 'init',
|
|
|
+ params?: any
|
|
|
+}
|
|
|
+const typeEnum = {
|
|
|
+ 'image': '图片',
|
|
|
+ 'news': '图文素材',
|
|
|
+ 'voice': '音频',
|
|
|
+ 'video': '视频',
|
|
|
+ 'knews': 'K-图文'
|
|
|
+}
|
|
|
+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',
|
|
|
+ fileType: 'image',
|
|
|
+ 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<Action>] = useReducer(reducer, initData)
|
|
|
+ const { fileName, sort, belongUser, fileType, parentId, selectFile, delPupId, rightClickPup, actionItem, path, publicPath, file, videoTitle, videoDescription } = state
|
|
|
+ const list = useAjax((params) => bdSysMediaList(params))
|
|
|
+ const add = useAjax((params) => bdSysMediaAdd(params), { msgNmae: '新增' })
|
|
|
+ const addWx = useAjax((params) => wxSysMediaAdd(params), { msgNmae: '新增' })
|
|
|
+ const del = useAjax((params) => delMedia(params), { msgNmae: '删除' })
|
|
|
+ const edit = useAjax((params) => bdSysMediaEdit(params), { msgNmae: '编辑' })
|
|
|
+ const sync = useAjax((params) => syncMedia(params), { msgNmae: '同步' })
|
|
|
+ const syncId = useAjax((params) => syncForSend(params), { msgNmae: '同步' })
|
|
|
+ const configSort = useAjax((params) => configSortApi(params), { msgNmae: '排序' })
|
|
|
+ const get_folder_tree = useAjax((params: any) => getFolderTree(params))
|
|
|
+ const edit_media_folder = useAjax((params: any) => editMediaFolder(params))
|
|
|
+ const get = useAjax((params) => getMedia(params))//获取图文详情
|
|
|
+ const eidtWxlists = useAjax((params) => editWxlist(params))
|
|
|
+ const [isOk, setIsOk] = useState<boolean>(true)
|
|
|
+ //请求上传地址
|
|
|
+ const fileUrl = useAjax((params: { type: string }) => 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 })
|
|
|
+ }
|
|
|
+ }, [])
|
|
|
+ /**新增K-客服模板消息 */
|
|
|
+ const knewsOk = useCallback((props: any) => {
|
|
|
+ let { title, knewsThumbUrl, knewsLink, description, knewsThumbId } = props?.newsList[0]
|
|
|
+ let obj = {
|
|
|
+ parentId,
|
|
|
+ belongUser,
|
|
|
+ folder: false,
|
|
|
+ fileType,
|
|
|
+ title: title,
|
|
|
+ knewsThumbUrl: knewsThumbUrl,
|
|
|
+ knewsLink: knewsLink,
|
|
|
+ description: description,
|
|
|
+ sort: props.sort
|
|
|
+ }
|
|
|
+ if (knewsThumbId) {//数据中存在封面图本地ID加入本地ID
|
|
|
+ obj['knewsThumbId'] = knewsThumbId
|
|
|
+ }
|
|
|
+ if (rightClickPup?.id && rightClickPup?.id !== 'all') {//编辑时传入ID
|
|
|
+ obj['sysMediaId'] = rightClickPup?.id
|
|
|
+ edit.run(obj).then((res) => {
|
|
|
+ if (res) {
|
|
|
+ list.refresh()
|
|
|
+ set({ knewsVisrible: false, knewsdefaultData: null })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ eidtWxlists.run({ sysMediaId: rightClickPup?.id, mpIds: props?.mpIds || [] }).then((res) => {
|
|
|
+ console.log(res)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ add.run({ ...obj, mpIds: props?.mpIds || [] }).then((res) => {
|
|
|
+ if (res) {
|
|
|
+ list.refresh()
|
|
|
+ set({ knewsVisrible: false, knewsdefaultData: null })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, [add, edit, list, parentId, belongUser, fileType, rightClickPup])
|
|
|
+ /**新增文件夹,非图文素材 */
|
|
|
+ const fileOk = useCallback((e: any, fnc?: () => Promise<any>, selectWx?: number[]) => {
|
|
|
+ e?.stopPropagation()
|
|
|
+ if (fnc) {//存在代表素材
|
|
|
+ fnc().then(res => {
|
|
|
+ if (!res) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (res?.file && file) {
|
|
|
+ /**修改文件名以用户设置的文件title命名*/
|
|
|
+ let newFile = new File([file], res?.fileName ? res?.fileName + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type })
|
|
|
+ let formData = new FormData();
|
|
|
+
|
|
|
+ if (fileType === 'voice') {
|
|
|
+ if (newFile.size > 2097152) {
|
|
|
+ message.error('请上传小于2M的音频')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (fileType === 'video' || fileType === 'image') {
|
|
|
+ if (newFile.size > 10485760) {
|
|
|
+ message.error('请上传小于10M的素材')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**向阿里云请求上传地址*/
|
|
|
+ fileUrl.run({ type: newFile.type }).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 (res) {
|
|
|
+ /**取到返回的文件地址向后端发送具体数据*/
|
|
|
+ if (res2?.data?.url) {
|
|
|
+ let fileMd5 = await getMD5(file)
|
|
|
+ let obj = { title: res?.fileName, fileType, folder: false, parentId, belongUser, url: res2?.data?.url, mpIds: selectWx, sort: res?.sort, fileMd5, fileSize: newFile?.size }
|
|
|
+ if (fileType === 'video') {
|
|
|
+ obj['videoTitle'] = res?.videoTitle || res?.title
|
|
|
+ obj['videoDescription'] = res?.videoDescription
|
|
|
+ }
|
|
|
+ add.run(obj).then((res) => {
|
|
|
+ console.log(res)
|
|
|
+ list.refresh()//刷新页面
|
|
|
+ offEditFile()//关闭弹窗并清空相关数据
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ message.error('上传失败!')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ if (fileName) {
|
|
|
+ let obj = { title: fileName, fileType, folder: true, parentId, belongUser, mpIds: selectWx, sort }
|
|
|
+ add.run(obj).then((res) => {
|
|
|
+ get_folder_tree.refresh()
|
|
|
+ list.refresh()//刷新页面
|
|
|
+ offEditFile()//关闭弹窗并清空相关数据
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [fileName, fileType, parentId, belongUser, list, file])
|
|
|
+ /**编辑非图文素材名称*/
|
|
|
+ const nameOk = useCallback((selectWx?: any) => {
|
|
|
+ if (fileName && actionItem) {
|
|
|
+ let obj = { title: fileName, belongUser, sysMediaId: actionItem?.id, fileType: actionItem?.fileType, folder: actionItem?.folder, url: actionItem?.url, sort }
|
|
|
+ if (fileType === 'video') {
|
|
|
+ obj['videoTitle'] = videoTitle
|
|
|
+ obj['videoDescription'] = videoDescription
|
|
|
+ }
|
|
|
+ edit.run(obj).then((res) => {
|
|
|
+ list.refresh()//刷新页面
|
|
|
+ offEditFile()//关闭弹窗并清空相关数据
|
|
|
+ })
|
|
|
+ }
|
|
|
+ //
|
|
|
+ eidtWxlists.run({ sysMediaId: actionItem?.id, mpIds: selectWx || [] }).then((res) => {
|
|
|
+ console.log(res)
|
|
|
+ })
|
|
|
+ }, [fileName, actionItem, edit, belongUser, fileType, 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(id).then(() => {
|
|
|
+ set({ selectFile: selectFile?.filter(i => i !== id) })//清理已删除文件
|
|
|
+ if (index === len - 1) {
|
|
|
+ list.refresh()//最后一次刷新页面
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, [list, selectFile])
|
|
|
+ /**获取本地素材数据列表 */
|
|
|
+ const getList = useCallback((props?: any) => {
|
|
|
+ let obj = { pageSize: 20, pageNum: 1, fileType, belongUser, parentId, ...props }
|
|
|
+ list.run(obj).then((res) => {
|
|
|
+ setIsOk(true)
|
|
|
+ })
|
|
|
+ }, [list, fileType, 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, fileType, 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, fileType, 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, fileType, belongUser, parentId: item.id })
|
|
|
+ console.log('文件夹被点击')
|
|
|
+ }, [path, fileType, 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 && fileType !== 'image') {//假如是文件不让选择
|
|
|
+ message.error('不能选择文件夹')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (state) {//存在就是删除
|
|
|
+ set({ selectFile: selectFile?.filter(i => i !== id) })
|
|
|
+ set({ selectItem: null })
|
|
|
+ } else {//否则新增
|
|
|
+ set({ selectFile: [id] })
|
|
|
+ set({ selectItem: item })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, [selectFile, fileType])
|
|
|
+ /**开启删除弹窗 */
|
|
|
+ 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?.fileType !== 'news') {//不是图文开启编辑名字弹窗
|
|
|
+ let obj = { fileVisible: true, actionItem: rightClickPup, fileName: rightClickPup.title, sort: rightClickPup.sort }
|
|
|
+ if (rightClickPup?.fileType === '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[] = []
|
|
|
+ list?.data?.records?.forEach((item: { id: any }) => {
|
|
|
+ allArr.push(item.id)
|
|
|
+ })
|
|
|
+ set({ selectFile: allArr.filter((i) => selectFile?.every(id => id !== i)) })
|
|
|
+ }, [selectFile, list])
|
|
|
+ /**图文素材弹窗开关 */
|
|
|
+ const showNews = useCallback(() => {
|
|
|
+
|
|
|
+ }, [])
|
|
|
+ return {
|
|
|
+ state,
|
|
|
+ init,
|
|
|
+ set,
|
|
|
+ fileOk,
|
|
|
+ getList,
|
|
|
+ dels,
|
|
|
+ onFile,
|
|
|
+ changeClickFile,
|
|
|
+ allFile,
|
|
|
+ delPupOn,
|
|
|
+ delPupOff,
|
|
|
+ editFile,
|
|
|
+ offEditFile,
|
|
|
+ nameOk,
|
|
|
+ fileClick,
|
|
|
+ treeClick,
|
|
|
+ pathClick,
|
|
|
+ knewsOk,
|
|
|
+ configSort,
|
|
|
+ fileUrl,
|
|
|
+ addWx,
|
|
|
+ list,
|
|
|
+ add,
|
|
|
+ sync,
|
|
|
+ get,
|
|
|
+ edit,
|
|
|
+ syncId,
|
|
|
+ typeEnum,
|
|
|
+ eidtWxlists,
|
|
|
+ get_folder_tree,
|
|
|
+ edit_media_folder
|
|
|
+ }
|
|
|
+}
|
|
|
+export default useBdMediaPup
|