wjx 2 years ago
parent
commit
5fd0422a44

+ 2 - 1
config/config.ts

@@ -2,7 +2,7 @@
 import { defineConfig } from 'umi';
 import defaultSettings from './defaultSettings';
 import proxy from './proxy';
-import {  launchSystem } from './routerConfig';
+import {  adMonitor, launchSystem } from './routerConfig';
 // const CompressionWebpackPlugin = require('compression-webpack-plugin');
 const { REACT_APP_ENV } = process.env;
 export default defineConfig({
@@ -53,6 +53,7 @@ export default defineConfig({
     // operatePage, //运营配置
     // dataStatistics, //数据中心配置
     launchSystem, // 投放系统
+    adMonitor, // 广告监控
     // enterpriseWeChat,//企业微信
     {
       path: '/',

+ 23 - 4
config/routerConfig.ts

@@ -10,7 +10,7 @@ function headrRouter(initialState: any, history: any) {
             ok = false
             let path = item?.routes?.length > 0 ? item?.routes[0]?.routes?.length > 0 ? item?.routes[0]?.routes[0]?.path ? item?.routes[0]?.routes[0]?.path : item?.routes[0].path : item?.routes[0].path : item?.routes[0].path
             if (history?.location?.pathname === '/') {
-                 history.push(path)
+                history.push(path)
             }
         }
     })
@@ -19,12 +19,18 @@ function headrRouter(initialState: any, history: any) {
         initialState?.menu?.data.forEach((item: { roles: string[], path: string, routes: { path: string, routes: any[] }[] }) => {
             if (item?.routes?.some((i: { path: string }) => i.path?.includes('/launchSystemNew'))) {
                 let path = item?.routes?.length > 0 ? (item?.routes[0]?.routes?.length > 0 && item?.routes[0]?.routes[0]?.path) ? item?.routes[0]?.routes[0]?.path : item?.routes[0].path : item?.routes[0].path
-                // history.push(path)
-                // window.open(location.origin+'/#'+path)
                 isPhone ? history.push(path) : window.open(location.origin+'/#'+path) 
             }
         })
-        // localStorage.setItem('bg', 'dark')
+    }
+    if (history?.location?.pathname === '/adMonitor') { //当切换一级菜单的辅助跳转
+        history?.goBack()
+        initialState?.menu?.data.forEach((item: { roles: string[], path: string, routes: { path: string, routes: any[] }[] }) => {
+            if (item?.routes?.some((i: { path: string }) => i.path?.includes('/adMonitor'))) {
+                let path = item?.routes?.length > 0 ? (item?.routes[0]?.routes?.length > 0 && item?.routes[0]?.routes[0]?.path) ? item?.routes[0]?.routes[0]?.path : item?.routes[0].path : item?.routes[0].path
+                isPhone ? history.push(path) : window.open(location.origin+'/#'+path) 
+            }
+        })
     }
 }
 //返回按钮路由
@@ -134,9 +140,22 @@ const launchSystem = {
         },
     ]
 }
+/** 广告监控 */
+const adMonitor = {
+    path: '/adMonitor',
+    routes: [
+        {
+            path: '/adMonitor/adMonitorList',
+            name: '广告监控',
+            component: './adMonitor/adMonitorList',
+            access: 'adMonitorList',
+        },
+    ]
+}
 
 export {
     headrRouter,
     getMyMenu,
     launchSystem,
+    adMonitor
 }

+ 2 - 2
src/Hook/useEcharts.tsx

@@ -586,7 +586,7 @@ function BarMonitor(props: {
                         color: '#3946c3',
                         fontSize: 16
                     },
-                    left: '45%',
+                    left: 'center',
                     top: 330,
                 },
                 tooltip: {
@@ -752,7 +752,7 @@ function LineMonitor(props: {
                 },
                 title: {
                     text: title || '',
-                    left: '45%',
+                    left: 'center',
                     top: 330,
                     textStyle: {
                         color: '#3946c3',

+ 116 - 37
src/components/CustomList/index.tsx

@@ -13,7 +13,7 @@ const SortableItem = SortableElement(({ item, del, setConfig }: any) => {
     return <li className='liDraw'>
         <div className='lileft'>
             <DragHandle />
-            <div>{item.title}</div>
+            <div>{item?.title}</div>
         </div>
         <Space style={{ float: 'right', marginRight: 10 }}>
             <Popover
@@ -39,7 +39,7 @@ const SortableItem = SortableElement(({ item, del, setConfig }: any) => {
                 <Tooltip title='宽度设置'><a><ColumnWidthOutlined /></a></Tooltip>
             </Popover>
         </Space>
-        {item.label !== 'pos_type' && <Tooltip title='删除'><div className="clear" onClick={() => { del() }}><a><CloseOutlined /></a></div></Tooltip>}
+        {item?.label !== 'pos_type' && <Tooltip title='删除'><div className="clear" onClick={() => { del() }}><a><CloseOutlined /></a></div></Tooltip>}
     </li>
 });
 /** 外层 */
@@ -54,7 +54,13 @@ interface dataProps {
 
 interface DataProps {
     label: string,
-    data: dataProps[]
+    data: dataProps[],
+    fieldSHow?: {
+        label: string,
+        saveField: string,
+        defaultValue: any[],
+        data: any[]
+    }
 }
 
 /**
@@ -68,9 +74,13 @@ type customProps = {
     configName: string,//配置表名称
     columns: any[],//配置菜单
     version: string, // 版本号
+    sysFixed?: {
+        left: number,
+        right: number
+    }
 }
 function CustomListModel(props: customProps) {
-    const { visible, onChange, onClose, config, configName, columns, version } = props
+    const { visible, onChange, onClose, config, configName, columns, version, sysFixed = { left: 0, right: 1 } } = props
     // dataA 总数据
     // 数据开始
     const [dataA, setDataA] = useState<DataProps[]>(config)//备份原始数据
@@ -78,46 +88,85 @@ function CustomListModel(props: customProps) {
     const [search, setSearch] = useState<string>('')  // 搜索字段
     const [selectData, setSelectData] = useState<dataProps[]>([])//选择的数据
     const [serverData, setServerData] = useState<dataProps[]>([])//备份的数据
-    const [fixed, setFixed] = useState<{ left: string, right: string }>(localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName) ? JSON.parse(localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName) as any) : { left: '0', right: '0' })
+    const [fieldData, setFieldData] = useState<any>({})
+    const [fixed, setFixed] = useState<{ left: number, right: number }>(localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName) ? JSON.parse(localStorage.getItem(`myAdMonitorConfigFixed${version}_` + configName) as any) : { left: sysFixed.left, right: sysFixed.right })
     // 数据结束
+
+    // 处理表格里列字段展示
+    useEffect(() => {
+        let mySelectFieldData = localStorage.getItem(`myAdFieldConfig${version}_` + configName)
+        let newSelectFieldData: any = {}
+        if (mySelectFieldData) {
+            newSelectFieldData = JSON.parse(mySelectFieldData)
+        }
+        data?.forEach((item) => {
+            if (item?.fieldSHow) {
+                const { saveField, defaultValue } = item?.fieldSHow
+                if (!newSelectFieldData[saveField]) {
+                    newSelectFieldData[saveField] = defaultValue
+                }
+            }
+        })
+        if (Object.keys(newSelectFieldData).length > 0) {
+            setFieldData({ ...newSelectFieldData })
+        }
+    }, [data])
     // 获取数据
     useEffect(() => {
         getUserList()
     }, [configName])
     // 获取个人配置
     const getUserList = () => {
-            let mySelectData = localStorage.getItem(`myAdMonitorConfig${version}_` + configName)
-            if (mySelectData) {//获取自定义配置
-                let newMySelectData = JSON.parse(mySelectData)
-                newMySelectData = newMySelectData.filter((item: any) => item && columns.some(c => c.dataIndex === item.dataIndex))//去除空项,并去除多余不存在的
-                setSelectData(newMySelectData)
-                setServerData(newMySelectData)
-                localStorage.setItem(`myAdMonitorConfig${version}_` + configName, JSON.stringify(newMySelectData))//重新存下本地,避免更新后数据不对
-            } else {//使用默认配置
-                let newSelectData: any[] = []
-                config?.forEach((item) => {
-                    item?.data?.forEach((d: { default: any }) => {
-                        if (d.default) {
-                            newSelectData[d.default - 1] = d
-                        }
-                    })
+        console.log('获取个人配置====>')
+        let mySelectData = localStorage.getItem(`myAdMonitorConfig${version}_` + configName)
+        if (mySelectData) {//获取自定义配置
+            let newMySelectData = JSON.parse(mySelectData)
+            newMySelectData = newMySelectData.filter((item: any) => item && columns.some(c => c.dataIndex === item.dataIndex))//去除空项,并去除多余不存在的
+            setSelectData(newMySelectData)
+            setServerData(newMySelectData)
+            setListWidth(newMySelectData)
+            localStorage.setItem(`myAdMonitorConfig${version}_` + configName, JSON.stringify(newMySelectData))//重新存下本地,避免更新后数据不对
+        } else {//使用默认配置
+            let newSelectData: any[] = []
+            config?.forEach((item) => {
+                item?.data?.forEach((d: { default: any }) => {
+                    if (d.default) {
+                        newSelectData[d.default - 1] = d
+                    }
                 })
-                setSelectData(newSelectData)
-                setServerData(newSelectData)
-            }
+            })
+            setSelectData(newSelectData)
+            setServerData(newSelectData)
+            setListWidth(newSelectData)
+        }
     }
-    //首次赋值默认宽
-    useEffect(() => {
+    console.log('selectData===>',)
+    // 首次赋值默认宽
+    const setListWidth = useCallback((selectData) => {
         if (selectData?.length === columns?.length && !selectData?.every((item: any) => item.width)) {
             let newSelectData: any[] = []
-            newSelectData = selectData?.map((item, index) => {
+            newSelectData = selectData?.map((item: { [x: string]: any }, index: string | number) => {
                 item['width'] = columns[index]['width']
                 return item
             })
             setSelectData(newSelectData)
             setServerData(newSelectData)
         }
-    }, [selectData, columns])
+    }, [columns])
+
+    //首次赋值默认宽
+    // useEffect(() => {
+    //     console.log('首次赋值默认宽====>',selectData,columns)
+    //     if (selectData?.length === columns?.length && !selectData?.every((item: any) => item.width)) {
+    //         let newSelectData: any[] = []
+    //         newSelectData = selectData?.map((item, index) => {
+    //             item['width'] = columns[index]['width']
+    //             return item
+    //         })
+    //         setSelectData(newSelectData)
+    //         setServerData(newSelectData)
+    //     }
+    // }, [selectData,columns])
     // 搜索 过滤
     const searchHandle = useCallback((value: string) => {
         setSearch(value)
@@ -160,9 +209,10 @@ function CustomListModel(props: customProps) {
     // 每个大项 全选
     const checkAllHandle = useCallback((checked: boolean, value: string) => {
         let oldData = data.find((item: DataProps) => item.label === value)
+        console.log(oldData)
         if (oldData) {
             if (checked) {
-                let newSelectData: any[] = selectData
+                let newSelectData: any[] = selectData?.filter(item => item)
                 oldData.data.forEach((item: dataProps) => {
                     if (!item.disabled && selectData.findIndex((selsectItem: dataProps) => selsectItem?.dataIndex === item?.dataIndex) === -1) {
                         newSelectData.push(item)
@@ -191,24 +241,26 @@ function CustomListModel(props: customProps) {
         if (selectData.length > 0) {
             localStorage.setItem(`myAdMonitorConfig${version}_` + configName, JSON.stringify(selectData))
             localStorage.setItem(`myAdMonitorConfigFixed${version}_` + configName, JSON.stringify(fixed))
+            localStorage.setItem(`myAdFieldConfig${version}_` + configName, JSON.stringify(fieldData))
             message.success('保存成功')
             onChange && onChange({ selectData, fixed })
             onClose && onClose()
         } else {
             message.error('请选择最少一项!!!')
         }
-    }, [serverData, selectData, fixed])
+    }, [serverData, selectData, fixed, fieldData])
     // 恢复默认
     const defaultConfig = useCallback(() => {
         localStorage.removeItem(`myAdMonitorConfig${version}_` + configName)
         localStorage.removeItem(`myAdMonitorConfigFixed${version}_` + configName)
-        if (configName.includes('起量')) {
-            setFixed({ left: '0', right: '2' })
-        }
+        setFixed({ ...sysFixed })
         getUserList()
+        message.success('恢复默认成功')
+        onChange && onChange('')
+        onClose && onClose()
     }, [])
     // 设置悬浮
-    const changeFixed = useCallback((name: string, value: string) => {
+    const changeFixed = useCallback((name: string, value: number) => {
         let v = Number(value)
         if (!isNaN(v)) {
             setFixed({ ...fixed, [name]: value })
@@ -229,6 +281,22 @@ function CustomListModel(props: customProps) {
         })
         setSelectData(newServerData)
     }, [selectData])
+
+    // 处理显示字段
+    const fieldHandle = (saveField: string, data: any, checked: boolean) => {
+        let newFieldData = JSON.parse(JSON.stringify(fieldData))
+        if (checked) {
+            if (Object.keys(newFieldData).includes(saveField)) {
+                newFieldData[saveField] = [...newFieldData[saveField], data]
+            } else {
+                newFieldData[saveField] = [data]
+            }
+        } else {
+            newFieldData[saveField] = newFieldData[saveField].filter((item: any) => item.key !== data.key && item.type === data.type)
+        }
+        setFieldData(newFieldData)
+    }
+
     return <Modal
         title={null}
         footer={null}
@@ -247,12 +315,23 @@ function CustomListModel(props: customProps) {
                     <Spin spinning={false}>
                         {data?.map((item: DataProps, index: number) => {
                             return <dl key={index}>
+                                {item?.fieldSHow && <div style={{ backgroundColor: '#f7f7f7' }}>
+                                    <dt style={{ color: '#ff7875' }}>{item.fieldSHow.label}</dt>
+                                    <div style={{ padding: '0 20px', boxSizing: 'border-box' }}>
+                                        {item?.fieldSHow?.data?.map((item1: any, index) => {
+                                            return <dl key={'field' + index}>
+                                                <dt>{item1.label}</dt>
+                                                {item1.data.map((item2: any, index: number) => <dd key={'item2' + index}><Checkbox className='checkbox' onChange={(e) => fieldHandle((item.fieldSHow as any).saveField, item2, e.target.checked)} checked={fieldData[(item.fieldSHow as any).saveField]?.some((field: any) => field.key === item2.key && field.type === item2.type)} disabled={fieldData[(item.fieldSHow as any).saveField]?.length > 0 ? fieldData[(item.fieldSHow as any).saveField][0].type === item1.label ? false : true : false}>{item2?.label}</Checkbox></dd>)}
+                                            </dl>
+                                        })}
+                                    </div>
+                                </div>}
                                 <dt>
                                     <Checkbox onChange={(e) => { checkAllHandle(e.target.checked, item.label) }} className='checkbox'>{item.label}</Checkbox>
                                 </dt>
                                 {item.data?.map((listItem: dataProps, listIndex: number) => {
                                     return <dd key={item.label + listIndex}>
-                                        <Checkbox onChange={(e) => { selectHandle(e.target.checked, listItem) }} disabled={listItem.label === 'pos_type' || listItem?.disabled} checked={selectData.some((selectItem: { dataIndex: string }) => selectItem.dataIndex === listItem.dataIndex)} className='checkbox'>{listItem.title}</Checkbox>
+                                        <Checkbox onChange={(e) => { selectHandle(e.target.checked, listItem) }} disabled={listItem.label === 'pos_type' || listItem?.disabled} checked={selectData.some((selectItem: { dataIndex: string }) => selectItem?.dataIndex === listItem?.dataIndex)} className='checkbox'>{listItem?.title}</Checkbox>
                                     </dd>
                                 })}
                             </dl>
@@ -262,12 +341,12 @@ function CustomListModel(props: customProps) {
             </div>
             <div className='right'>
                 <div className='rightTitle'>
-                    <div className="rightTitleLeft">已选<span>{selectData?.length}</span>项</div>
+                    <div className="rightTitleLeft">已选<span>{selectData?.filter(item => item)?.length}</span>项</div>
                     <a onClick={defaultConfig} style={{ marginRight: 30 }}>恢复默认</a>
                 </div>
                 <div style={{ display: 'flex', flexFlow: 'row nowarp', padding: '0 28px', justifyContent: 'space-between' }}>
-                    <div>左侧固定列数:<InputNumber size='small' style={{ width: 50 }} onChange={(value: string) => { changeFixed('left', value) }} value={fixed.left} max={'100'} min={'0'} /></div>
-                    <div>右侧固定列数:<InputNumber size='small' style={{ width: 50 }} onChange={(value: string) => { changeFixed('right', value) }} value={fixed.right} max={'100'} min={'0'} /></div>
+                    <div>左侧固定列数:<InputNumber size='small' style={{ width: 50 }} onChange={(value: number) => { changeFixed('left', value) }} value={fixed.left} max={100} min={0} /></div>
+                    <div>右侧固定列数:<InputNumber size='small' style={{ width: 50 }} onChange={(value: number) => { changeFixed('right', value) }} value={fixed.right} max={100} min={0} /></div>
                 </div>
                 <SortableList axis='y' onSortEnd={onSortEnd} useDragHandle>
                     {selectData.map((item: dataProps, index: number) => <SortableItem key={'li' + index} index={index} item={item} del={() => { selectHandle(false, item) }} setConfig={setConfig} />)}

+ 65 - 0
src/models/useAdMonitor/useMonitor.ts

@@ -0,0 +1,65 @@
+import { useAjax } from '@/Hook/useAjax'
+import {
+    ListType, getPlanListApi, getTotalCostApi, getPlanCostApi, getCostSpeedApi, getUserGroupApi, getAllPlanListApi,
+    allPlanProps, getDetailListApi, getMinuteListApi, downLoadUpAdApi, downLoadDetailApi,
+    downLoadDetailMinuteApi, downLoadSpeedApi, downLoadAllAdListApi, addEditGroupApi, getAdGroupListApi, deleteAdGroupApi, 
+    getAccountListApi, AccountListProps, addDelAccountApi, getBookListAllApi, getAdqAccountListApi
+} from '@/services/adMonitor/adMonitor'
+
+
+export default function useMonitor() {
+    /** 获取起量计划列表 */
+    const getPlanList = useAjax((params: ListType) => getPlanListApi(params), { formatResult: true })
+    /**获取起来计划列表明细*/
+    const getPlanDetailList = useAjax((params: ListType) => getDetailListApi(params), { formatResult: true })
+    /**获取起来计划列表5min*/
+    const getMinuteList = useAjax((params: ListType) => getMinuteListApi(params), { formatResult: true })
+    /** 获取今日计划总消耗图谱 */
+    const getTotalCost = useAjax((params: ListType) => getTotalCostApi(params), { formatResult: true })
+    /** 获取计划消耗图谱 */
+    const getPlanCost = useAjax((params: ListType) => getPlanCostApi(params), { formatResult: true })
+    /** 获取消耗速度详情(弹窗) */
+    const getCostSpeed = useAjax((params: ListType) => getCostSpeedApi(params), { formatResult: true })
+    /** 获取组员 */
+    const getUserGroup = useAjax(() => getUserGroupApi(), { formatResult: true })
+    /** 计划列表 */
+    const getAllPlanList = useAjax((params: allPlanProps) => getAllPlanListApi(params), { formatResult: true })
+    const getBookListAll = useAjax(() => getBookListAllApi(), { formatResult: true })
+    /** 下载 */
+    const downLoadUpAd = useAjax((params: ListType) => downLoadUpAdApi(params), { formatResult: true })
+    const downLoadDetail = useAjax((params: ListType) => downLoadDetailApi(params), { formatResult: true })
+    const downLoadDetailMinute = useAjax((params: ListType) => downLoadDetailMinuteApi(params), { formatResult: true })
+    const downLoadSpeed = useAjax((params: ListType) => downLoadSpeedApi(params), { formatResult: true })
+    const downLoadAllAdList = useAjax((params: allPlanProps) => downLoadAllAdListApi(params), { formatResult: true })
+    // 监控数据源
+    const addEditGroup = useAjax((params: { groupName: string, id?: number, remark?: string }) => addEditGroupApi(params), { formatResult: true })
+    const getAdGroupList = useAjax(() => getAdGroupListApi(), { formatResult: true })
+    const deleteAdGroup = useAjax((params: { id: number }) => deleteAdGroupApi(params), { formatResult: true })
+    const getAccountList = useAjax((params: AccountListProps) => getAccountListApi(params), { formatResult: true })
+    const addDelAccount = useAjax((params: { accountIdList: number[], groupId: number, type: 0 | 1 }) => addDelAccountApi(params), { formatResult: true })
+
+    // 获取广告账号列表
+    const getAdqAccountList = useAjax(() => getAdqAccountListApi(), { formatResult: true })
+    return {
+        getPlanList,
+        getTotalCost,
+        getPlanCost,
+        getCostSpeed,
+        getUserGroup,
+        getAllPlanList,
+        getPlanDetailList,
+        getMinuteList,
+        downLoadUpAd,
+        downLoadDetail,
+        downLoadDetailMinute,
+        downLoadSpeed,
+        downLoadAllAdList,
+        addEditGroup,
+        getAdGroupList,
+        deleteAdGroup,
+        getAccountList,
+        addDelAccount,
+        getBookListAll,
+        getAdqAccountList
+    }
+}

+ 3 - 1
src/models/useOperating/useWxGroupList.ts

@@ -165,7 +165,9 @@ export default function useWxGroupList() {
       //     dispatch({ type: 'allWx', params: { allWx } });
       //   }
       // });
-      // getAllZhMemBer.run()
+      getAllZhMemBer.run().then(res => {
+        dispatch({ type: 'allOfMember', params: { allOfMember: res } });
+      })
       // getAllOfMember.run().then((res) => {
       //   let groupAllWx: any[] = [];
       //   res?.forEach((item: any) => {

+ 50 - 0
src/pages/adMonitor/adMonitorList/components/box.tsx

@@ -0,0 +1,50 @@
+import { Popover, Space } from 'antd'
+import React, { useMemo } from 'react'
+
+function Box(props: { b: any }) {
+    const { b } = props
+    let el = useMemo(() => {
+        if (b?.imagePreviewUrl?.length > 0 || b?.videoKeyFrameImageUrl) {//图片存在? 或者视频首针图存在?
+            return <div className='imagesConfig'>
+                <Popover placement='right' content={
+                    <div>
+                        {b?.title && <div style={{ maxWidth: 300 }}><strong style={{ fontSize: 15 }}>标题:</strong>{b?.title}</div>}
+                        <Space style={{ maxWidth: 300, display: 'flex',  flexFlow: 'row wrap',margin:'10px 0' }}>
+                            {
+                                b?.imagePreviewUrl?.length > 0 ? b?.imagePreviewUrl?.map((img: string | undefined) => {
+                                    return <img src={img} key={img} width={b?.imagePreviewUrl?.length === 1 ? 200 : b?.imagePreviewUrl?.length === 3 ? 70 : b?.imagePreviewUrl?.length === 4 ? 100 : 70} />
+                                }) : <img src={b?.videoKeyFrameImageUrl} width={200} />
+                            }
+                        </Space>
+                        {b?.description && <small style={{ fontSize: 10, maxWidth: 300, display: 'inline-block' }}><strong style={{ fontSize: 13 }}>描述:</strong>{b?.description}</small>}
+                    </div>
+                }>
+                    <img src={b?.imagePreviewUrl ?  b?.imagePreviewUrl[0] : b?.videoKeyFrameImageUrl} width={24} />
+                </Popover>
+            </div>
+        } else if (b?.videoPreviewUrl) {//视频存在?
+            return <Popover placement='right' content={
+                <div>
+                    {b?.title && <div style={{ maxWidth: 300 }}><strong style={{ fontSize: 15 }}>标题:</strong>{b?.title}</div>}
+                    <video src='https://img-baofun.zhhainiao.com/pcwallpaper_ugc/preview/b946235d4e4f078cdfe5099736e1dbc8_preview.mp4' style={{ maxWidth: 300 }} controls />
+                    {b?.description && <small style={{ fontSize: 10, maxWidth: 300, display: 'inline-block' }}><strong style={{ fontSize: 13 }}>描述:</strong>{b?.description}</small>}
+                </div>
+            }>
+                <video src='https://img-baofun.zhhainiao.com/pcwallpaper_ugc/preview/b946235d4e4f078cdfe5099736e1dbc8_preview.mp4' style={{ width: 24 }} />
+            </Popover>
+        } else {
+            return <Popover placement='right' content={
+                <div>
+                     {b?.title && <div style={{ maxWidth: 300 }}><strong style={{ fontSize: 15 }}>标题:</strong>{b?.title}</div>}
+                    {b?.description && <small style={{ fontSize: 10, maxWidth: 300, display: 'inline-block' }}><strong style={{ fontSize: 13 }}>描述:</strong>{b?.description}</small>}
+                </div>
+            }>
+                <div style={{width:'100%',height:'100%'}}>
+                    --
+                </div>
+            </Popover>
+        }
+    }, [b])
+    return el
+}
+export default React.memo(Box)

+ 184 - 0
src/pages/adMonitor/adMonitorList/components/planDetail.tsx

@@ -0,0 +1,184 @@
+import Tables from "@/components/Tables";
+import useEcharts from "@/Hook/useEcharts";
+import { CloudDownloadOutlined, RedoOutlined } from "@ant-design/icons";
+import { Button, Card, DatePicker, Modal, Radio, Space, Spin, Statistic, Tag } from "antd";
+import moment, { Moment } from "moment";
+import React, { useCallback, useEffect, useState } from "react";
+import { useModel } from "umi";
+import columns from './tableConfig'
+import { compare } from '@/utils/utils'
+import { formatDate, downloadFile1 } from '@/utils/downloadFile'
+import { downLoadSpeedApi } from '@/services/launch/adMonitor'
+
+type Props = {
+    visible?: boolean,
+    onClose?: () => void,
+    data?: any,
+    dataStartTime?: string
+    dataEndTime?: string
+}
+
+function PlanDetail(props: Props) {
+    const { adgroupId, putUserId, accountId } = props.data
+    const { getCostSpeed } = useModel('useAdMonitor.useMonitor')
+
+    /** 变量start */
+    const { visible, onClose, dataStartTime, dataEndTime } = props
+    const { LineMonitor } = useEcharts()
+    const [lineDis, setLineDis] = useState<any[]>([])
+    const [queryForm, setQueryForm] = useState<any>({ adgroupId, accountId, sysUserId: putUserId, timeUnit: dataStartTime || dataEndTime ? 'day' : 'hour', dataStartTime: dataStartTime || moment().format('YYYY-MM-DD'), dataEndTime: dataEndTime || moment().format('YYYY-MM-DD') })
+    const [speedData, setSpeedData] = useState<{ totalCost: string, viewCount: string, clickCount: string, clickRate: string, orderRate: string, costSpeed: string, adCostSpeedVOList: any[] }>()
+    const [adCostSpeedDtoListOri, setAdCostSpeedDtoListOri] = useState<any[]>([])
+    const [downLoadLoading, setDownLoadLoading] = useState<boolean>(false)
+    /** 变量end */
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+    const getList = useCallback(() => {
+        getCostSpeed.run(queryForm).then((res: any) => {
+            if (res?.data) {
+                setSpeedData(() => res?.data)
+                setAdCostSpeedDtoListOri(() => [...res?.data?.adCostSpeedVOList])
+                let spendData: any = { legendName: '花费' }
+                let exposureData: any = { legendName: '曝光次数' }
+                res?.data?.adCostSpeedVOList?.reverse()?.forEach((item: { day: string, time: string, cost: string, viewCount: string }) => {
+                    if (queryForm.timeUnit === 'hour') {
+                        spendData[item?.time] = item?.cost
+                        exposureData[item?.time] = item?.viewCount
+                    } else {
+                        spendData[item?.day] = item?.cost
+                        exposureData[item?.day] = item?.viewCount
+                    }
+                });
+                setLineDis(() => [spendData, exposureData])
+            }
+        })
+    }, [queryForm, adCostSpeedDtoListOri, speedData])
+    // 设置获取图标数据区间 设置时间
+    const setTime = useCallback((str: string[]) => {
+        setQueryForm({ ...queryForm, dataStartTime: str[0], dataEndTime: str[1] })
+    }, [queryForm])
+
+    const disabledDate = (date: Moment) => {
+        if (queryForm?.timeUnit === 'hour') {
+            return date && (date < moment().startOf('day').subtract(9, 'day') || date > moment().endOf('day'))
+        } else {
+            return false
+        }
+    }
+
+    const downLoadExcel = useCallback(() => {
+        setDownLoadLoading(true)
+        downLoadSpeedApi(queryForm).then(res => {
+            setDownLoadLoading(false)
+            downloadFile1(res, 'octet-stream', formatDate(new Date()) + ".xlsx")
+        }).catch(() => setDownLoadLoading(false))
+    }, [queryForm, downLoadLoading])
+
+    return <Modal
+        title={<><Space>
+            <span style={{ fontSize: 15, color: '#999' }}>计划名称:<span style={{ color: '#40a9ff' }}>{props?.data?.campaignName}</span></span>
+            &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+            <span style={{ fontSize: 15, color: '#999' }}>广告名称:<span style={{ color: '#40a9ff' }}>{props?.data?.adgroupName}</span></span>
+        </Space></>}
+        visible={visible}
+        width={900}
+        bodyStyle={{ padding: '0 5px', backgroundColor: '#f2f2f2' }}
+        footer={null}
+        onCancel={() => { onClose && onClose() }}
+    >
+        <Space direction='vertical' style={{ width: '100%', marginBottom: 10 }}>
+            <Card hoverable size='small'>
+                <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                    <Space>
+                        <DatePicker.RangePicker disabledDate={disabledDate} allowClear={false} onChange={(mo: any, str: string[]) => { setTime(str) }} value={(queryForm?.dataStartTime && queryForm?.dataEndTime ? [moment(queryForm?.dataStartTime), moment(queryForm?.dataEndTime)] : null) as any} />
+                        <Radio.Group
+                            value={queryForm?.timeUnit}
+                            buttonStyle="solid"
+                            size='small'
+                            onChange={(e) => {
+                                if (e.target.value === 'hour') {
+                                    setQueryForm({ ...queryForm, timeUnit: e.target.value, dataStartTime: moment().format('YYYY-MM-DD'), dataEndTime: moment().format('YYYY-MM-DD') })
+                                } else {
+                                    setQueryForm({ ...queryForm, timeUnit: e.target.value })
+                                }
+                            }}>
+                            {/* <Radio.Button value="minute">5min</Radio.Button> */}
+                            <Radio.Button value="day">分天</Radio.Button>
+                            <Radio.Button value="hour">小时</Radio.Button>
+                        </Radio.Group>
+                    </Space>
+                    <Space style={{ marginRight: 30 }}>
+                        <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getCostSpeed?.data?.reqTime}</span>
+                        <Tag color="#2db7f5" onClick={() => { getList() }}><RedoOutlined />   刷新</Tag>
+                    </Space>
+                </div>
+            </Card>
+            <Card
+                hoverable
+                title='数据展示'
+            >
+                <Spin spinning={getCostSpeed?.loading}>
+                    <div className='overviewData'>
+                        <div>
+                            <div>总消耗</div>
+                            <Statistic value={speedData?.totalCost || 0} valueStyle={{ color: '#03A613', fontSize: 16 }} />
+                        </div>
+                        <div>
+                            <div>曝光次数</div>
+                            <Statistic value={speedData?.viewCount || 0} valueStyle={{ fontSize: 16 }} />
+                        </div>
+                        <div>
+                            <div>点击次数</div>
+                            <Statistic value={speedData?.clickCount || 0} valueStyle={{ fontSize: 16 }} />
+                        </div>
+                        <div>
+                            <div>点击率</div>
+                            <Statistic value={(speedData?.clickRate ? Number(speedData?.clickRate) * 100 : 0).toFixed(2)} precision={2} valueStyle={{ color: '#3f8600', fontSize: 16 }} suffix="%" />
+                        </div>
+                        <div>
+                            <div>所选时间内平均消耗速度</div>
+                            <Statistic value={speedData?.costSpeed ? Number(speedData?.costSpeed).toFixed(2) : 0} valueStyle={{ color: '#cf1322', fontSize: 16 }} />
+                        </div>
+                    </div>
+                </Spin>
+            </Card>
+            <Card
+                hoverable
+                title='趋势图'
+            >
+                <div style={{ width: '100%', height: 260, textAlign: 'center' }}>
+                    {getCostSpeed?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: 260 }} series smooth data={lineDis} />}
+                </div>
+            </Card>
+            <Card
+                hoverable
+                title={<Space>
+                    <div>明细表</div>
+                    <Button size="small" icon={<CloudDownloadOutlined />} loading={downLoadLoading} onClick={downLoadExcel}>下载</Button>
+                </Space>}
+            >
+                <Tables
+                    columns={columns(queryForm?.timeUnit)}
+                    dataSource={speedData?.adCostSpeedVOList || []}
+                    total={speedData?.adCostSpeedVOList?.length}
+                    loading={getCostSpeed?.loading}
+                    size="small"
+                    bordered
+                    sortDirections={['ascend', 'descend', null]}
+                    onChange={(pagination: any, filters: any, sorter: any) => {
+                        if (JSON.stringify(sorter) !== '{}') {
+                            let { field, order } = sorter   // descend 降序 大到小  ascend 升序 小到大
+                            if (JSON.stringify(speedData) !== '{}') {
+                                setSpeedData({ ...speedData as any, adCostSpeedVOList: order ? speedData?.adCostSpeedVOList?.sort(compare(field, order)) : [...adCostSpeedDtoListOri] })
+                            }
+                        }
+                    }}
+                />
+            </Card>
+        </Space>
+
+    </Modal>
+}
+
+export default React.memo(PlanDetail)

+ 73 - 0
src/pages/adMonitor/adMonitorList/components/tableConfig.tsx

@@ -0,0 +1,73 @@
+import { Statistic } from "antd"
+import React from "react"
+let columns = (timeUnit: string) => [
+    {
+        title: '时间',
+        dataIndex: 'time',
+        key: 'time',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{fontSize: "12px"}}>{timeUnit === 'hour' ? b?.time : b?.day}</span>
+        }
+    },
+    {
+        title: '曝光次数',
+        dataIndex: 'viewCount',
+        key: 'viewCount',
+        align: 'center',
+        sorter: true
+    },
+    {
+        title: '点击次数',
+        dataIndex: 'clickCount',
+        key: 'clickCount',
+        align: 'center',
+        width: 120,
+        sorter: true,
+        render: (a: any, b: any) => {
+            return <span style={{fontSize: "12px"}}>{a}</span>
+        }
+    },
+    {
+        title: '点击率',
+        dataIndex: 'clickRate',
+        key: 'clickRate',
+        align: 'center',
+        sorter: true,
+        render: (a: any, b: any) => {
+            return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+        }
+    },
+    {
+        title: '下单量',
+        dataIndex: 'orderCount',
+        key: 'orderCount',
+        align: 'center',
+        sorter: true,
+        render: (a: any, b: any) => {
+            return <span style={{fontSize: "12px"}}>{a}</span>
+        }
+    },
+    {
+        title: '下单率',
+        dataIndex: 'orderRate',
+        key: 'orderRate',
+        align: 'center',
+        sorter: true,
+        render: (a: any, b: any) => {
+            return <Statistic value={a ? (a * 100).toFixed(2) : 0} precision={2} valueStyle={{ color: '#3f8600' }} suffix="%" />
+        }
+    },
+    {
+        title: '消耗',
+        dataIndex: 'cost',
+        key: 'cost',
+        align: 'center',
+        sorter: true,
+        render: (a: any, b: any) => {
+            return <Statistic value={a || 0} />
+        }
+    },
+]
+
+export default columns

+ 276 - 0
src/pages/adMonitor/adMonitorList/config.ts

@@ -0,0 +1,276 @@
+/**起量表原始列表*/
+const qiliangpaihang = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '时间', dataIndex: 'day', label: '设置信息', default: 1, width: 120 },
+            { title: '广告名称', dataIndex: 'adgroupName', label: '设置信息', default: 2, width: 170 },
+            { title: '广告ID', dataIndex: 'adgroupId', label: '设置信息', default: 9, width: 90 },
+            { title: '计划名称', dataIndex: 'campaignName', label: '设置信息' },
+            { title: '计划ID', dataIndex: 'campaignId', label: '设置信息' },
+            { title: '广告账户', dataIndex: 'accountId', label: '设置信息', default: 10, width: 70 },
+            { title: '投手', dataIndex: 'putUserName', label: '设置信息', default: 11, width: 65 },
+            { title: '创意预览', dataIndex: 'creativePreview', label: '设置信息', default: 12, width: 80 },
+            // { title: '投放位置', dataIndex: 'pitchSeat', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息' },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', width: 145 },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息' },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectName', label: '设置信息' },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 60 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'dailyBudget', label: '费用', width: 80 },
+            // { title: '出价方式', dataIndex: 'biddingMethod', label: '费用' },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用', default: 24, width: 110 },
+            { title: '今日消耗', dataIndex: 'costDay', label: '费用', default: 15, width: 100 },
+            { title: '当前小时消耗', dataIndex: 'costHour', label: '费用', default: 16, width: 100 },
+            { title: '前第1小时消耗', dataIndex: 'costLastHour', label: '费用', default: 17, width: 100 },
+            { title: '前第2小时消耗', dataIndex: 'costLastTwoHour', label: '费用', default: 18, width: 100 },
+            { title: '前第3小时消耗', dataIndex: 'costLastThreeHour', label: '费用', default: 19, width: 100 },
+            { title: '当前小时消耗差额', dataIndex: 'costDiffBeforeHour', label: '费用', default: 20, width: 100 },
+            { title: '前第1小时消耗差额', dataIndex: 'costDiffBeforeTwoHour', label: '费用', default: 21, width: 100 },
+            { title: '前第2小时消耗差额', dataIndex: 'costDiffBeforeThreeHour', label: '费用', default: 22, width: 100 },
+            { title: '前三小时消耗趋势', dataIndex: 'costTrendLastThreeHour', label: '费用', default: 23, width: 70 },
+            { title: '当前5min消耗流速', dataIndex: 'costSpeed', label: '费用', default: 14, width: 80 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'viewDay', label: '曝光', default: 3, width: 70 },
+            { title: '千次曝光成本', dataIndex: 'thousandDisplayPriceDay', label: '曝光', default: 4, width: 80 },
+        ]
+    },
+    {
+        label: '点击',
+        data: [
+            { title: '点击量(点击次数)', dataIndex: 'clickDay', label: '点击', width: 75 },
+            { title: '点击均价', dataIndex: 'cpcDay', label: '点击', width: 75 },
+            { title: '点击率', dataIndex: 'ctrDay', label: '点击', width: 75 },
+        ]
+    },
+    {
+        label: '转化指标组',
+        data: [
+            { title: '优化目标(转化目标)', dataIndex: 'optimizationGoal', label: '转化指标组', width: 120 },
+            { title: '转化目标量', dataIndex: 'conversionsCountDay', label: '转化指标组', default: 7, width: 75 },
+            { title: '转化目标成本', dataIndex: 'conversionsCostDay', label: '转化指标组', default: 8, width: 75 },
+            { title: '目标转化率', dataIndex: 'conversionsRateDay', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'orderCountDay', label: '商品转化', width: 70 },
+            { title: '首日新增下单量', dataIndex: 'firstDayOrderCountDay', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'orderCostDay', label: '商品转化', width: 75 },
+            { title: '下单率', dataIndex: 'orderRateDay', label: '商品转化', width: 70 },
+            { title: '下单金额', dataIndex: 'orderAmountDay', label: '商品转化', width: 70 },
+            { title: '首日新增下单金额', dataIndex: 'firstDayOrderAmountDay', label: '商品转化', width: 75 },
+            { title: '下单客单价', dataIndex: 'atvDay', label: '商品转化', width: 70 },
+            { title: '下单ROI', dataIndex: 'orderRoiDay', label: '商品转化', width: 75 },
+            { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiDay', label: '商品转化', default: 5, width: 90 },
+        ]
+    },
+    {
+        label: '公众号转化',
+        data: [
+            { title: '公众号关注人数', dataIndex: 'mpFollowUvDay', label: '公众号转化', default: 6, width: 85 },
+            { title: '公众号关注率', dataIndex: 'mpFollowRateDay', label: '公众号转化', width: 70 },
+            { title: '公众号关注成本', dataIndex: 'mpFollowCostDay', label: '公众号转化', width: 85 },
+            // { title: '注册人数', dataIndex: 'mpRegisterUserCount', label: '公众号转化' },
+            // { title: '注册次数', dataIndex: 'mpRegisterCount', label: '公众号转化' },
+            // { title: '注册成本', dataIndex: 'mpRegisterCost', label: '公众号转化' },
+        ]
+    },
+]
+
+/**起量表分钟原始列表*/
+const qiliangpaihangminute = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '时间', dataIndex: 'day', label: '设置信息', default: 1, width: 120 },
+            { title: '广告名称', dataIndex: 'adgroupName', label: '设置信息', default: 2, width: 170 },
+            { title: '广告ID', dataIndex: 'adgroupId', label: '设置信息', default: 9, width: 90 },
+            { title: '计划名称', dataIndex: 'campaignName', label: '设置信息' },
+            { title: '计划ID', dataIndex: 'campaignId', label: '设置信息' },
+            { title: '广告账户', dataIndex: 'accountId', label: '设置信息', default: 10, width: 70 },
+            { title: '投手', dataIndex: 'putUserName', label: '设置信息', default: 11, width: 65 },
+            { title: '创意预览', dataIndex: 'creativePreview', label: '设置信息', default: 12, width: 80 },
+            // { title: '投放位置', dataIndex: 'pitchSeat', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息' },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', width: 145 },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息' },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectName', label: '设置信息' },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 60 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'dailyBudget', label: '费用', width: 80 },
+            // { title: '出价方式', dataIndex: 'biddingMethod', label: '费用' },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用', width: 110, disabled: true },
+            { title: '今日消耗', dataIndex: 'costDay', label: '费用', width: 100, disabled: true },
+            { title: '当前小时消耗', dataIndex: 'costHour', label: '费用', width: 100, disabled: true },
+            { title: '前第1小时消耗', dataIndex: 'costLastHour', label: '费用', width: 100, disabled: true },
+            { title: '前第2小时消耗', dataIndex: 'costLastTwoHour', label: '费用', width: 100, disabled: true },
+            { title: '前第3小时消耗', dataIndex: 'costLastThreeHour', label: '费用', width: 100, disabled: true },
+            { title: '当前小时消耗差额', dataIndex: 'costDiffBeforeHour', label: '费用', width: 100, disabled: true },
+            { title: '前第1小时消耗差额', dataIndex: 'costDiffBeforeTwoHour', label: '费用', width: 100, disabled: true },
+            { title: '前第2小时消耗差额', dataIndex: 'costDiffBeforeThreeHour', label: '费用', width: 100, disabled: true },
+            { title: '前三小时消耗趋势', dataIndex: 'costTrendLastThreeHour', label: '费用', width: 100, disabled: true },
+            { title: '当前5min消耗流速', dataIndex: 'costSpeed', label: '费用', default: 14, width: 80 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'viewDay', label: '曝光', default: 3, width: 70 },
+            { title: '千次曝光成本', dataIndex: 'thousandDisplayPriceDay', label: '曝光', default: 4, width: 80 },
+        ]
+    },
+    {
+        label: '点击',
+        data: [
+            { title: '点击量(点击次数)', dataIndex: 'clickDay', label: '点击', width: 75 },
+            { title: '点击均价', dataIndex: 'cpcDay', label: '点击', width: 75 },
+            { title: '点击率', dataIndex: 'ctrDay', label: '点击', width: 75 },
+        ]
+    },
+    {
+        label: '转化指标组',
+        data: [
+            { title: '优化目标(转化目标)', dataIndex: 'optimizationGoal', label: '转化指标组', width: 120 },
+            { title: '转化目标量', dataIndex: 'conversionsCountDay', label: '转化指标组', default: 7, width: 75 },
+            { title: '转化目标成本', dataIndex: 'conversionsCostDay', label: '转化指标组', default: 8, width: 75 },
+            { title: '目标转化率', dataIndex: 'conversionsRateDay', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'orderCountDay', label: '商品转化', width: 70 },
+            { title: '首日新增下单量', dataIndex: 'firstDayOrderCountDay', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'orderCostDay', label: '商品转化', width: 75 },
+            { title: '下单率', dataIndex: 'orderRateDay', label: '商品转化', width: 70 },
+            { title: '下单金额', dataIndex: 'orderAmountDay', label: '商品转化', width: 70 },
+            { title: '首日新增下单金额', dataIndex: 'firstDayOrderAmountDay', label: '商品转化', width: 75 },
+            { title: '下单客单价', dataIndex: 'atvDay', label: '商品转化', width: 70 },
+            { title: '下单ROI', dataIndex: 'orderRoiDay', label: '商品转化', width: 75 },
+            { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiDay', label: '商品转化', default: 5, width: 90 },
+        ]
+    },
+    {
+        label: '公众号转化',
+        data: [
+            { title: '公众号关注人数', dataIndex: 'mpFollowUvDay', label: '公众号转化', default: 6, width: 85 },
+            { title: '公众号关注率', dataIndex: 'mpFollowRateDay', label: '公众号转化', width: 85 },
+            { title: '公众号关注成本', dataIndex: 'mpFollowCostDay', label: '公众号转化', width: 85 },
+            // { title: '注册人数', dataIndex: 'mpRegisterUserCount', label: '公众号转化' },
+            // { title: '注册次数', dataIndex: 'mpRegisterCount', label: '公众号转化' },
+            // { title: '注册成本', dataIndex: 'mpRegisterCost', label: '公众号转化' },
+        ]
+    },
+]
+
+/**广告原始列表*/
+const guanggao = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '数据日期', dataIndex: 'beginToEnd', label: '设置信息', default: 1, width: 110 },
+            { title: '广告名称/ID', dataIndex: 'adgroupName', label: '设置信息', default: 2 },
+            { title: '投放计划名称/ID', dataIndex: 'campaignName/campaignId', label: '设置信息', default: 3 },
+            { title: '创意名称/ID', dataIndex: 'creativeName/creativeId', label: '设置信息', default: 4 },
+            { title: '创意预览', dataIndex: 'creativePreview', label: '设置信息', default: 5, width: 80 },
+            { title: '投手', dataIndex: 'putUserName', label: '设置信息', default: 6 },
+            { title: '创建日期', dataIndex: 'adCreateTime', label: '设置信息' },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息', default: 7, width: 75 },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息', default: 11 },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息', default: 15 },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', default: 8, width: 90 },
+            // { title: '当日成本偏差', dataIndex: 'dayCostOffset', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectName', label: '设置信息', default: 9 },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'adBudget', label: '费用', default: 10, width: 90 },
+            { title: '出价方式', dataIndex: 'bidMode', label: '费用' },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用' },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用', default: 19 },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用' },
+            // { title: '今日消耗', dataIndex: 'todayCostTotal', label: '费用' },
+            { title: '单位时间消耗速度(元/小时)', dataIndex: 'costSpeed', label: '费用', default: 11, width: 80 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'viewCount', label: '曝光' },
+            { title: '千次曝光成本', dataIndex: 'thousandDisplayPriceTotal', label: '曝光' },
+        ]
+    },
+    {
+        label: '点击',
+        data: [
+            { title: '点击量(点击次数)', dataIndex: 'clickCount', label: '点击', width: 75 },
+            { title: '点击均价', dataIndex: 'avgClickAmount', label: '点击', width: 75 },
+            { title: '点击率', dataIndex: 'clickRate', label: '点击', default: 12, width: 70 },
+        ]
+    },
+    {
+        label: '转化指标组',
+        data: [
+            { title: '优化目标(转化目标)', dataIndex: 'optimizationGoal', label: '转化指标组', width: 120 },
+            { title: '转化目标量', dataIndex: 'conversionsCount', label: '转化指标组', width: 75 },
+            { title: '转化目标成本', dataIndex: 'conversionsCost', label: '转化指标组', width: 75 },
+            { title: '目标转化率', dataIndex: 'conversionsRate', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'orderCount', label: '商品转化', width: 70 },
+            { title: '首日新增下单量', dataIndex: 'firstDayOrderCountTotal', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'orderCost', label: '商品转化', width: 75 },
+            { title: '下单率', dataIndex: 'orderRate', label: '商品转化', width: 70 },
+            { title: '下单金额', dataIndex: 'orderAmount', label: '商品转化', width: 70 },
+            { title: '首日新增下单金额', dataIndex: 'firstDayOrderAmountTotal', label: '商品转化', width: 75 },
+            { title: '下单客单价', dataIndex: 'atvAmount', label: '商品转化', width: 70 },
+            { title: '下单ROI', dataIndex: 'orderROI', label: '商品转化', width: 75 },
+            { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiTotal', label: '商品转化', width: 90 },
+        ]
+    },
+    {
+        label: '公众号转化',
+        data: [
+            { title: '公众号关注人数', dataIndex: 'mpFollowUser', label: '公众号转化', width: 85 },
+            { title: '公众号关注率', dataIndex: 'mpFollowRate', label: '公众号转化', width: 85 },
+            { title: '公众号关注成本', dataIndex: 'mpFollowCost', label: '公众号转化', width: 85 },
+        ]
+    },
+]
+
+export { qiliangpaihang, guanggao, qiliangpaihangminute }

+ 116 - 0
src/pages/adMonitor/adMonitorList/enum.ts

@@ -0,0 +1,116 @@
+/**购买类型*/
+enum GOUMAILEIXING {
+    'BILLINGEVENT_CLICK' = '按点击扣费',
+    'BILLINGEVENT_APP_DOWNLOAD' = '按照应用下载扣费',
+    'BILLINGEVENT_IMPRESSION' = '按曝光扣费'
+}
+/**广告状态*/
+enum GUANGGAOZHUANGTAI {
+    'STATUS_PENDING' = '审核中',
+    'STATUS_DENIED' = '审核不通过',
+    'STATUS_SUSPEND' = '暂停中',
+    'STATUS_READY' = '未到投放时间',
+    'STATUS_ACTIVE' = '投放中',
+    'STATUS_STOP' = '投放结束',
+    'STATUS_ACTIVE_CAMPAIGN_SUSPEND' = '广告被暂停',
+    'STATUS_PART_READY' = '部分待投放',
+    'STATUS_PART_ACTIVE' = '部分投放中',
+    'STATUS_DELETED' = '已删除',
+    'STATUS_UNKNOWN' = '未知状态',
+    'STATUS_FROZEN' = '冻结',
+    'STATUS_PREPARE' = '准备中'
+
+}
+/**创意状态*/
+enum CHUANGYIZHUANGTAI {
+    'AD_GROUP_STATUS_NORMAL' =
+    '有效',
+    'AD_GROUP_STATUS_PENDING' =
+    '待审核',
+    'AD_GROUP_STATUS_DENIED' =
+    '审核不通过',
+    'AD_GROUP_STATUS_FROZEN' =
+    '封停',
+    'AD_GROUP_STATUS_PARTIALLY_PENDING' =
+    '部分审核中',
+    'AD_GROUP_STATUS_PARTIALLY_NORMAL' =
+    '部分有效',
+    'AD_GROUP_STATUS_PREPARE' =
+    '准备中(渠道包在审核中)',
+    'AD_GROUP_STATUS_DELETED' =
+    '已删除',
+    'AD_GROUP_STATUS_INVALID' =
+    '失效(渠道包审核不通过,请更新渠道包)'
+}
+/**推广目标*/
+enum TUIGUANGMUBIAO {
+    'PRODUCTTYPE_WECHAT_ARTICLE' = '推广品牌活动',
+    'PRODUCTTYPE_WECHAT_LBS' = '推广门店',
+    'PRODUCTTYPE_LEAD_AD' = '收集销售线索',
+    'PRODUCTTYPE_WECHAT_SHOP' = '推广商品',
+    'PRODUCTTYPE_APPLE_APP_STORE' = '推广应用',
+    'PRODUCTTYPE_WECHAT_CARD' = '派发优惠券',
+    'PRODUCTTYPE_WECHAT' = '推广公众号',
+    'PRODUCTTYPE_WECHAT_MINI_GAME' = '推广小游戏',
+}
+/**优化目标*/
+enum YOUHUAMUBIAO {
+    "OPTIMIZATIONGOAL_NONE" = "none",
+    "OPTIMIZATIONGOAL_BRAND_CONVERSION" = "指定页面曝光",
+    "OPTIMIZATIONGOAL_FOLLOW" = "关注",
+    "OPTIMIZATIONGOAL_CLICK" = "点击",
+    "OPTIMIZATIONGOAL_IMPRESSION" = "曝光",
+    "OPTIMIZATIONGOAL_APP_DOWNLOAD" = "下载",
+    "OPTIMIZATIONGOAL_APP_ACTIVATE" = "激活",
+    "OPTIMIZATIONGOAL_APP_REGISTER" = "注册",
+    "OPTIMIZATIONGOAL_ONE_DAY_RETENTION" = "次日留存",
+    "OPTIMIZATIONGOAL_APP_PURCHASE" = "付费次数,游戏客户如需优化付费行为,建议使用首次付费作为优化目标",
+    "OPTIMIZATIONGOAL_ECOMMERCE_ORDER" = "下单",
+    "OPTIMIZATIONGOAL_ECOMMERCE_CHECKOUT" = "H5 付费次数(待废弃)",
+    "OPTIMIZATIONGOAL_LEADS" = "表单预约(微信流量,待废弃)",
+    "OPTIMIZATIONGOAL_ECOMMERCE_CART" = "加入购物车",
+    "OPTIMIZATIONGOAL_PROMOTION_CLICK_KEY_PAGE" = "H5 注册(待废弃)",
+    "OPTIMIZATIONGOAL_VIEW_COMMODITY_PAGE" = "商品详情页浏览",
+    "OPTIMIZATIONGOAL_ONLINE_CONSULTATION" = "在线咨询",
+    "OPTIMIZATIONGOAL_TELEPHONE_CONSULTATION" = "电话拨打",
+    "OPTIMIZATIONGOAL_PAGE_RESERVATION" = "表单预约",
+    "OPTIMIZATIONGOAL_DELIVERY" = "发货",
+    "OPTIMIZATIONGOAL_MESSAGE_AFTER_FOLLOW" = "公众号内发消息",
+    "OPTIMIZATIONGOAL_CLICK_MENU_AFTER_FOLLOW" = "公众号内点击菜单栏",
+    "OPTIMIZATIONGOAL_PAGE_EFFECTIVE_ONLINE_CONSULT" = "有效在线咨询 (已废弃)",
+    "OPTIMIZATIONGOAL_PAGE_EFFECTIVE_PHONE_CALL" = "有效电话拨打 (已废弃)",
+    "OPTIMIZATIONGOAL_CONFIRM_EFFECTIVE_LEADS_CONSULT" = "有效在线咨询(待废弃)",
+    "OPTIMIZATIONGOAL_CONFIRM_EFFECTIVE_LEADS_PHONE" = "有效电话拨打(待废弃)",
+    "OPTIMIZATIONGOAL_LEADS_COLLECT" = "综合线索收集",
+    "OPTIMIZATIONGOAL_FIRST_PURCHASE" = "首次付费",
+    "OPTIMIZATIONGOAL_APPLY" = "进件",
+    "OPTIMIZATIONGOAL_PRE_CREDIT" = "预授信",
+    "OPTIMIZATIONGOAL_CREDIT" = "授信",
+    "OPTIMIZATIONGOAL_WITHDRAW_DEPOSITS" = "提现",
+    "OPTIMIZATIONGOAL_PROMOTION_VIEW_KEY_PAGE" = "关键页面访问",
+    "OPTIMIZATIONGOAL_MOBILE_APP_CREATE_ROLE" = "小游戏创角",
+    "OPTIMIZATIONGOAL_CANVAS_CLICK" = "跳转按钮点击",
+    "OPTIMIZATIONGOAL_PROMOTION_CLAIM_OFFER" = "领券",
+    "OPTIMIZATIONGOAL_ECOMMERCE_ADD_TO_WISHLIST" = "商品收藏",
+    "OPTIMIZATIONGOAL_CONFIRM_EFFECTIVE_LEADS_RESERVATION" = "有效表单预约(待废弃)",
+    "OPTIMIZATIONGOAL_PAGE_RECEIPT" = "签收",
+    "OPTIMIZATIONGOAL_PAGE_SCAN_CODE" = "加企业微信客服",
+    "OPTIMIZATIONGOAL_SELECT_COURSE" = "选课",
+    "OPTIMIZATIONGOAL_CONFIRM_POTENTIAL_CUSTOMER_PHONE" = "电话潜在客户",
+    "OPTIMIZATIONGOAL_MOBILE_APP_AD_INCOME" = "广告变现",
+    "OPTIMIZATIONGOAL_MOBILE_APP_ACCREDIT" = "小游戏授权",
+    "OPTIMIZATIONGOAL_PURCHASE_MEMBER_CARD" = "首次会员购买",
+    "OPTIMIZATIONGOAL_PAGE_CONFIRM_EFFECTIVE_LEADS" = "有效综合线索",
+    "OPTIMIZATIONGOAL_ADD_DESKTOP" = "快应用加桌面",
+    "OPTIMIZATIONGOAL_RESERVATION" = "微信流量预约行为",
+    "OPTIMIZATIONGOAL_FIRST_ECOMMERCE_ORDER" = "首次下单",
+    "OPTIMIZATIONGOAL_FIRST_TWENTY_FOUR_HOUR_ECOMMERCE_ORDER" = "24 小时下单",
+    "OPTIMIZATIONGOAL_LIKE" = "点赞",
+    "OPTIMIZATIONGOAL_EXTERNAL_LINK_CLICK" = "外链点击",
+    "OPTIMIZATIONGOAL_BUY_COUPONS" = "购券",
+    "OPTIMIZATIONGOAL_LEAVE_INFORMATION" = "留资",
+    "OPTIMIZATIONGOAL_CORE_ACTION" = "关键行为",
+    "OPTIMIZATIONGOAL_ONE_DAY_RETENTION_RATIO" = "次留率",
+    "OPTIMIZATIONGOAL_PROMOTION_READ_ARTICLE" = "阅读文章"
+}
+export { GOUMAILEIXING, GUANGGAOZHUANGTAI, CHUANGYIZHUANGTAI, TUIGUANGMUBIAO, YOUHUAMUBIAO }

+ 110 - 0
src/pages/adMonitor/adMonitorList/index.less

@@ -0,0 +1,110 @@
+.adMonitorList {
+    color: #65B581;
+    color: #FFCE34;
+    color: #ff544b;
+    color: rgba(236, 128, 141);
+}
+
+.adMonitorListTab {
+  .ant-tabs-nav {
+    margin-bottom: 0;
+  }
+}
+
+
+
+//  monitor
+.monitor {
+    .charts {
+        display: flex;
+        justify-content: space-between;
+        flex-wrap: wrap;
+        >div {
+            width: 49.5%;
+            // min-width: 600px;
+            min-height: 360px;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            text-align: center;
+            position: relative;
+            &>.spin {
+                width: 100%;
+                height: 100%;
+                &>div.ant-spin-container{
+                    width: 100%;
+                    height: 100%; 
+                    &>div{
+                        width: 100%;
+                        height: 100%; 
+                    }
+                }
+            }
+            .selectTime {
+                position: absolute;
+                top: 0;
+                right: 70px;
+                z-index: 10;
+            }
+        }
+    }
+    .charts100{
+        >div{
+            width: 100% !important;
+        }
+    }
+}
+
+
+
+.overviewData {
+    display: flex;
+    justify-content: space-between;
+    >div {
+        min-width: 145px;
+        padding: 10px;
+        text-align: center;
+        border: 1px solid rgb(233, 233, 233);
+        border-radius: 4px;
+
+        >div:first-child{
+            font-weight: 600;
+            margin-bottom: 4px;
+        }
+    }
+}
+
+.imagesConfig {
+    height: 100%; 
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    >div{
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        &:first-child {
+            z-index: 1;
+        }
+    }
+}
+
+.oneText {
+    white-space: nowrap;      /*超出的空白区域不换行*/
+    overflow: hidden;         /*超出隐藏*/
+    text-overflow: ellipsis;  /*文本超出显示省略号*/
+    padding: 0 4px;
+    font-size: 12px;
+}
+.twoText {
+    text-overflow: -o-ellipsis-lastline;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    line-clamp: 2;
+    -webkit-box-orient: vertical;
+    font-size: 12px;
+}

+ 31 - 0
src/pages/adMonitor/adMonitorList/index.tsx

@@ -0,0 +1,31 @@
+import { Tabs } from "antd"
+import React, { useState } from "react"
+import { useModel } from "umi"
+import Monitor from "./monitor"
+import PlanList from "./planList"
+
+
+const AdMonitorList: React.FC = () => {
+    // 变量开始
+    const [tab, setTab] = useState<string>('monitor')  // tab切换
+    const { getPlanList, getPlanDetailList, getAllPlanList } = useModel('useAdMonitor.useMonitor')
+    // 变量结束
+
+    return <div className="adMonitorList">
+        <Tabs activeKey={tab} className="adMonitorListTab" type="card" onChange={(activeKey: string) => {
+            if (activeKey === 'monitor') {
+                getAllPlanList.mutate([])
+            } else {
+                getPlanList.mutate([])
+                getPlanDetailList.mutate([])
+            }
+            setTab(activeKey)
+        }}>
+            <Tabs.TabPane tab="今日起量广告监控" key="monitor" />
+            <Tabs.TabPane tab="广告列表" key="list" />
+        </Tabs>
+        {tab === 'monitor' ? <Monitor onChange={() => { setTab('list') }} /> : tab === 'list' ? <PlanList /> : null}
+    </div>
+}
+
+export default AdMonitorList

+ 444 - 0
src/pages/adMonitor/adMonitorList/monitor.tsx

@@ -0,0 +1,444 @@
+import { Button, Card, Input, Radio, RadioChangeEvent, Select, Space, Spin, Tag, TimePicker, Tooltip } from "antd";
+import React, { useCallback, useEffect, useState } from "react";
+import { CloudDownloadOutlined, ColumnHeightOutlined, ColumnWidthOutlined, RedoOutlined } from "@ant-design/icons";
+import useEcharts from '@/Hook/useEcharts'
+import columnsMonitor from './tableMonitorConfig'
+import { useModel } from 'umi'
+import { ListType, downLoadUpAdApi, downLoadDetailApi, downLoadDetailMinuteApi } from '@/services/adMonitor/adMonitor'
+import { qiliangpaihang, qiliangpaihangminute } from './config'
+import { compare } from '@/utils/utils'
+import PlanDetail from './components/planDetail'
+import { formatDate, downloadFile1 } from '@/utils/downloadFile'
+import './table.less'
+import moment from "moment";
+import TableData from "@/pages/launchSystemNew/components/TableData";
+interface newListType extends ListType {
+    totalTimeUnit: 'minute' | 'hour' | 'day',
+    planTimeUnit: 'minute' | 'hour' | 'day'
+}
+
+/**
+ * 今日起量监控
+ * @param props 
+ * @returns 
+ */
+function Monitor(props: { onChange: () => void }) {
+    const { onChange } = props
+    const { getPlanList, getTotalCost, getPlanCost, getPlanDetailList, getMinuteList, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
+    const { state } = useModel('useOperating.useWxGroupList')
+    // 变量开始
+    const [queryForm, setQueryForm] = useState<newListType>({ totalTimeUnit: 'day', planTimeUnit: 'hour', pageNum: 1, pageSize: 20 }) // 搜索变量//startTime: moment().format('YYYY-MM-DD'), endTime: moment().format('YYYY-MM-DD'),
+    const { BarMonitor, LineMonitor } = useEcharts()
+    const [barDis, setBarDis] = useState<any[]>([])
+    const [lineDis, setLineDis] = useState<any[]>([])
+    const [lineTitle, setLineTitle] = useState<string>('广告总消耗趋势')
+    const [datas, setDatas] = useState<any[]>([])  // 所有数据
+    const [pichers, setPichers] = useState<any[]>([]) // 投手数据
+    const [mode, setMode] = useState<string>('total') // 总/明细
+    const [px, setPx] = useState<boolean>(false)//设置顶部图形的排列方式
+    const [planDetailList, setPlanDetailList] = useState<any[]>([])
+    const [minuteList, setMinuteList] = useState<any[]>([])
+    const [visible, setVisible] = useState<boolean>(false) // 详情弹窗控制
+    const [aId, setAId] = useState<any>()
+    const [downLoadLoading, setDownLoadLoading] = useState<boolean>(false)
+    const { totalTimeUnit, planTimeUnit, adgroup, accountId, sysUserId, pageNum, pageSize, campaign, sortField, sort } = queryForm
+
+    // 变量结束
+    useEffect(() => {
+        if (state?.allOfMember?.length > 0) {//组长数据存在使用组长数据
+            setDatas(state?.allOfMember)
+        } else if (state?.myallOfUser?.length > 0) {//否则个人
+            setDatas(state?.myallOfUser)
+        }
+    }, [state])
+    // 获取广告账号
+    useEffect(() => {
+        getAdqAccountList.run()
+    }, [])
+    // 处理投手
+    useEffect(() => {
+        if (datas.length > 0) {
+            let new_pichers = datas?.map((item: { key: { nickname: string, userId: number } }) => ({ nickName: item?.key?.nickname, userId: item?.key?.userId }))
+            if (JSON.stringify(pichers) !== JSON.stringify(new_pichers)) {//避免重复设置触发条件多次请求接口
+                setPichers(new_pichers)
+            }
+        }
+    }, [datas, pichers])
+    // // 获取排行数据,柱图
+    useEffect(() => {
+        if (pichers.length > 0) {
+            getPlanCostList()
+        }
+    }, [totalTimeUnit, accountId, sysUserId, pichers])
+    // 获取起量计划列表 底部table
+    useEffect(() => {
+        if (pichers.length > 0) {
+            if (mode === 'total') {
+                getList()
+            } else if (mode === 'detail') {
+                if (adgroup) {
+                    getDetailList(adgroup)
+                }
+            } else if (mode === 'minute') {
+                if (adgroup) {
+                    getMinuList(adgroup)
+                }
+            }
+        }
+    }, [accountId, sysUserId, pageNum, pageSize, campaign, pichers, adgroup, sortField, sort, mode])
+    // 获取今日计划总消耗图谱,折线
+    useEffect(() => {
+        if (pichers.length > 0) {
+            getTootalCostList()
+        }
+    }, [planTimeUnit, adgroup, accountId, sysUserId, pichers])
+    // 获取起量明细表 点击
+    useEffect(() => {
+        // if (adgroup) {
+        //     getDetailList(adgroup)
+        // } else {
+        //     setMode('total')//切到总表
+        // }
+        if (!adgroup) {
+            setMode('total')//切到总表
+        }
+    }, [adgroup])
+    /** 获取折线图 */
+    const getTootalCostList = useCallback(async () => {
+        let { totalTimeUnit, planTimeUnit, pageNum, pageSize, adgroup, sysUserId, accountId, ...newQueryForm } = queryForm
+        let params = adgroup ? { ...newQueryForm, adgroupId: adgroup } : newQueryForm
+        let newPitcherIds = sysUserId?.join()
+        let res = await getTotalCost.run({ ...params, timeUnit: planTimeUnit, sysUserId: newPitcherIds, accountId: accountId?.join() })
+        let data: any[] = [{ legendName: '消耗' }]
+        res?.data?.totalCostDtoList?.forEach((item: any) => {
+            data[0][item.currTime] = item.totalCost
+        })
+        setLineDis(() => data)
+        setLineTitle(() => res?.data?.adName ? res?.data?.adName + '_消耗趋势' : '广告总消耗趋势')
+    }, [queryForm, lineDis, pichers])
+
+    /** 获取柱状图 */
+    const getPlanCostList = useCallback(async () => {
+        let { totalTimeUnit, planTimeUnit, pageNum, pageSize, sysUserId, accountId, ...newQueryForm } = queryForm
+        let { adgroup, ...planQueryFrom } = newQueryForm
+        let newPitcherIds = sysUserId?.join()
+        let res = await getPlanCost.run({ ...planQueryFrom, timeUnit: totalTimeUnit, sysUserId: newPitcherIds, accountId: accountId?.join() })
+        let data = res?.data?.planCostDtoList?.map((item: { adId: number, cost: number, adName: string }) => {
+            return { name: item.adId.toString(), value: item.cost, adName: item.adName }
+        })
+        data = data?.sort((a: any, b: any) => {
+            var value1 = a['value'];
+            var value2 = b['value'];
+            return value2 - value1;
+        })
+        setBarDis(() => data)
+    }, [queryForm, barDis, pichers])
+
+    // 气量Table总表
+    const getList = useCallback(() => {
+        let { totalTimeUnit, planTimeUnit, timeUnit, sysUserId, accountId, ...newQueryForm } = queryForm
+        getPlanList.run({ ...newQueryForm, sysUserId, accountId: accountId?.join() })
+    }, [queryForm, pichers])
+
+
+    // 起量明细表 点击
+    const getDetailList = useCallback((adgroup: any) => {
+        let { totalTimeUnit, planTimeUnit, timeUnit, pageNum, pageSize, sysUserId, accountId, ...newQueryForm } = queryForm
+        if (adgroup) {
+            setMode('detail')//切到明细
+            getPlanDetailList.run({ ...newQueryForm, pageNum: 1, pageSize: 20, adgroupId: adgroup, sysUserId, accountId: accountId?.join() }).then((res: any) => {
+                setPlanDetailList(res?.data?.records ? [...res?.data?.records] : [])
+                setQueryForm({ ...queryForm, adgroup })
+            })
+        }
+    }, [queryForm, getPlanList, pichers, planDetailList])
+
+    // 详情
+    const details = (data: any) => {
+        setAId(data)
+        setVisible(true)
+    }
+
+    // 起量5min表
+    const getMinuList = useCallback((adgroup: any) => {
+        let { totalTimeUnit, planTimeUnit, timeUnit, pageNum, pageSize, sysUserId, accountId, ...newQueryForm } = queryForm
+        if (adgroup) {
+            setMode('minute')
+            getMinuteList.run({ ...newQueryForm, pageNum: 1, pageSize: 20, adgroupId: adgroup, sysUserId, accountId: accountId?.join() }).then((res: any) => {
+                setMinuteList(res?.data?.records ? [...res?.data?.records] : [])
+                setQueryForm({ ...queryForm, adgroup })
+            })
+        }
+    }, [queryForm, getPlanList, pichers, minuteList, adgroup])
+
+    // 计划详情
+    const planDetail = (data: any) => {
+        sessionStorage.setItem('ADIDORNAME', JSON.stringify(data))
+        onChange && onChange()
+    }
+
+    //全部接口刷新
+    const refresh = () => {
+        getPlanList.refresh()
+        getTotalCost.refresh()
+        getPlanCost.refresh()
+        getPlanDetailList.refresh()
+        getMinuteList.refresh()
+    }
+    // 接口自动刷新10分钟一次
+    useEffect(() => {
+        let time = setInterval(() => {
+            refresh()
+        }, 1000 * 60 * 10)
+        return () => {
+            clearInterval(time)
+        }
+    }, [pichers])
+    //图形排列样式改变重新获取数据刷新图形
+    const set = useCallback((b) => {
+        setPx(b)
+        getTotalCost.refresh()
+        getPlanCost.refresh()
+    }, [getPlanCost, getTotalCost])
+
+    // 下载
+    const downLoadExcel = useCallback(() => {
+        // downLoadUpAdApi, downLoadDetailApi, downLoadDetailMinuteApi
+        let ajax: any = null
+        let params: any = {}
+        let { totalTimeUnit, planTimeUnit, timeUnit, sysUserId, accountId, adgroup, pageNum, pageSize, ...newQueryForm } = queryForm
+        let newPitcherIds = sysUserId?.join()
+        switch (mode) {
+            case 'total':
+                params = { ...newQueryForm, sysUserId: newPitcherIds, accountId: accountId?.join() }
+                ajax = downLoadUpAdApi
+                break;
+            case 'detail':
+                params = { ...newQueryForm, adgroup, sysUserId: newPitcherIds, accountId: accountId?.join() }
+                ajax = downLoadDetailApi
+                break;
+            case 'minute':
+                params = { ...newQueryForm, adgroup, sysUserId: newPitcherIds, accountId: accountId?.join() }
+                ajax = downLoadDetailMinuteApi
+                break;
+        }
+        if (ajax) {
+            setDownLoadLoading(true)
+            ajax(params).then((res: any) => {
+                setDownLoadLoading(false)
+                downloadFile1(res, 'octet-stream', formatDate(new Date()) + ".xlsx")
+            }).catch(() => setDownLoadLoading(false))
+        }
+    }, [queryForm, mode, pichers, downLoadLoading])
+
+    // 处理折线图数据
+    const timePickerHandle = async (values: any, formatString: [string, string]) => {
+        let res = await getTotalCost.data
+        let data: any[] = [{ legendName: '消耗' }]
+        res?.data?.totalCostDtoList?.forEach((item: any) => {
+            data[0][item.currTime] = item.totalCost
+        })
+        if (values) {
+            let date = moment().format('YYYY-MM-DD')
+            let strTime = formatString[0]
+            let endTime = formatString[1]
+            let dateTimeStr = `${date} ${strTime}:00`
+            let dateTimeEnd = `${date} ${endTime}:00`
+            let { legendName, ...otherData } = data[0]
+            let newData: any = { legendName }
+            for (const key in otherData) {
+                if (Object.prototype.hasOwnProperty.call(otherData, key)) {
+                    const value = otherData[key];
+                    if (moment(dateTimeStr) <= moment(key) && moment(key) <= moment(dateTimeEnd)) {
+                        newData[key] = value
+                    }
+                }
+            }
+            setLineDis(() => [newData])
+        } else {
+            setLineDis(() => data)
+        }
+    }
+
+    return <Space direction='vertical' style={{ width: '100%' }} className="monitor">
+        <Card hoverable >
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <Space>
+                    <Select
+                        showSearch
+                        value={queryForm.sysUserId}
+                        style={{ minWidth: 180 }}
+                        mode='multiple'
+                        maxTagCount={1}
+                        disabled={queryForm?.accountId?.length > 0}
+                        allowClear placeholder="请选择投手"
+                        onChange={(value: string) => { setQueryForm({ ...queryForm, sysUserId: value, pageNum: 1 }) }}
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                    >
+                        {pichers?.map((item: { nickName: string, userId: number }, index: number) =>
+                            <Select.Option
+                                value={item.userId}
+                                key={item.userId + '' + index}
+                            >
+                                {item.nickName}
+                            </Select.Option>
+                        )}
+                    </Select>
+                    <Select
+                        showSearch
+                        mode='multiple'
+                        maxTagCount={1}
+                        value={queryForm.accountId}
+                        style={{ minWidth: 220 }}
+                        allowClear
+                        placeholder="请选择广告账号"
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                        onChange={(value: string) => {
+                            setQueryForm({ ...queryForm, accountId: value, pageNum: 1 })
+                        }}
+                    >
+                        {getAdqAccountList?.data?.data?.map((item: { id: number, accountId: number, wechatAccountName: string }) => <Select.Option
+                            value={item.accountId}
+                            key={item.id}
+                        >
+                            {item.accountId + "_" + item.wechatAccountName}
+                        </Select.Option>)}
+                    </Select>
+                    <Input value={queryForm.campaign} placeholder="计划ID" onChange={(e) => {
+                        let value = e.target.value
+                        if (!isNaN(Number(value))) {
+                            setQueryForm({ ...queryForm, campaign: value })
+                        }
+                    }}
+                        allowClear />
+                    <Input value={queryForm.adgroup} placeholder="广告ID" onChange={(e) => {
+                        let value = e.target.value
+                        if (!isNaN(Number(value))) {
+                            setQueryForm({ ...queryForm, adgroup: e.target.value })
+                        }
+                    }
+                    } allowClear />
+                </Space>
+                <Space>
+                    <Tag color="#2db7f5" onClick={refresh}><RedoOutlined />   刷新</Tag>
+                </Space>
+            </div>
+        </Card>
+        <Card hoverable>
+            <span style={{ position: 'absolute', top: 10 }}>
+                {px ?
+                    <Tooltip title='左右排列'><Tag color='#f50' onClick={() => { set(false) }}><ColumnWidthOutlined /><span style={{ fontSize: 10 }}>左右排列</span></Tag></Tooltip>
+                    :
+                    <Tooltip title='上下排列'><Tag color='#f50' onClick={() => { set(true) }} ><ColumnHeightOutlined /><span style={{ fontSize: 10 }}>上下排列</span></Tag></Tooltip>
+                }
+            </span>
+            <div className={!px ? 'charts' : 'charts charts100'}>
+                <div>
+                    <div className="selectTime">
+                        <Space>
+                            <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getPlanCost?.data?.reqTime}</span>
+                            <Radio.Group value={queryForm.totalTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, totalTimeUnit: e.target.value }) }}>
+                                <Radio.Button value="day">天</Radio.Button>
+                                <Radio.Button value="hour">小时</Radio.Button>
+                                <Radio.Button value="minute">5min</Radio.Button>
+                            </Radio.Group>
+                        </Space>
+                    </div>
+                    {getPlanCost?.loading ? <Spin /> : <BarMonitor style={{ width: '100%', height: '100%' }} data={barDis} xName="今日消耗" yName="广告名称" onChange={(value?: string) => { setQueryForm({ ...queryForm, adgroup: value }); setMode('detail') }} planID={queryForm?.adgroup} />}
+                </div>
+                <div>
+                    <div className="selectTime">
+                        <Space>
+                            <span style={{ fontSize: 10, color: '#999' }}>刷新时间:{getTotalCost?.data?.reqTime}</span>
+                            <Radio.Group value={queryForm.planTimeUnit} buttonStyle="solid" size='small' onChange={(e) => { setQueryForm({ ...queryForm, planTimeUnit: e.target.value }) }}>
+                                <Radio.Button value="hour">小时</Radio.Button>
+                                <Radio.Button value="minute">5min</Radio.Button>
+                            </Radio.Group>
+                            {queryForm.planTimeUnit === 'minute' && <TimePicker.RangePicker
+                                size="small"
+                                format='HH:mm'
+                                minuteStep={5}
+                                onChange={timePickerHandle}
+                            />}
+                        </Space>
+                    </div>
+                    {getTotalCost?.loading ? <Spin /> : <LineMonitor style={{ width: '100%', height: '100%' }} series smooth data={lineDis} title={lineTitle} />}
+                </div>
+            </div>
+        </Card>
+        <div className={'MYtable'}>
+            <TableData
+                columns={columnsMonitor(planDetail, getDetailList, details, getMinuList, mode)}
+                dataSource={mode === 'total' ? getPlanList?.data?.data?.records?.map((item: any, index: number) => ({ ...item, id: item.id + '_' + index })) : mode === 'detail' ? getPlanDetailList?.data?.data?.records?.map((item: any, index: number) => ({ ...item, id: item.id + '_' + index })) : getMinuteList?.data?.data?.records?.map((item: any, index: number) => ({ ...item, id: item.id + '_' + index }))}
+                loading={mode === 'total' ? getPlanList?.loading : mode === 'detail' ? getPlanDetailList?.loading : getMinuteList?.loading}
+                ajax={mode === 'total' ? getPlanList : mode === 'detail' ? getPlanDetailList : getMinuteList}
+                leftChild={
+                    <Space>
+                        <Radio.Group onChange={(e: RadioChangeEvent) => {
+                            let value = e.target.value
+                            setMode(value)
+                        }} value={mode} size='small'>
+                            <Radio.Button value="total">总</Radio.Button>
+                            <Radio.Button value="detail" disabled={!adgroup}>小时</Radio.Button>
+                            <Radio.Button value="minute" disabled={!adgroup}>5min</Radio.Button>
+                        </Radio.Group>
+                        <Button size="small" icon={<CloudDownloadOutlined />} loading={downLoadLoading} onClick={downLoadExcel}>{mode === 'total' ? '总表' : mode === 'detail' ? '小时表' : '5min表'}下载</Button>
+                    </Space>
+                }
+                fixed={{ left: 0, right: 2 }}
+                total={mode === 'total' ? getPlanList?.data?.data?.total : 0}
+                onChange={mode === 'total' ? (props: any) => {
+                    let { sortData, pagination } = props
+                    let { current, pageSize } = pagination
+                    let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                    newQueryForm.pageNum = current
+                    newQueryForm.pageSize = pageSize
+                    if (sortData && JSON.stringify('sortData') !== '{}') {
+                        let { field, order } = sortData   // descend 降序 大到小  ascend 升序 小到大
+                        if (order) {
+                            newQueryForm.sortField = field
+                            newQueryForm.sort = order === 'ascend' ? 'ASC' : 'DESC'
+                        } else {
+                            Object.keys(newQueryForm).forEach(key => {
+                                if (key === 'sortField' || key === 'sort') {
+                                    delete newQueryForm[key]
+                                }
+                            })
+                        }
+                    } else {
+                        Object.keys(newQueryForm).forEach(key => {
+                            if (key === 'sortField' || key === 'sort') {
+                                delete newQueryForm[key]
+                            }
+                        })
+                    }
+                    setQueryForm({ ...newQueryForm })
+                } : (props: any) => {
+                    let { sortData } = props
+                    if (sortData && JSON.stringify('sortData') !== '{}') {
+                        let { field, order } = sortData   // descend 降序 大到小  ascend 升序 小到大 planDetailList
+                        if (mode === 'detail') {
+                            getPlanDetailList?.mutate({ records: order ? getPlanDetailList?.data?.data?.records?.sort(compare(field, order)) : [...planDetailList] })
+                        } else if (mode === 'minute') {
+                            getMinuteList?.mutate({ records: order ? getMinuteList?.data?.data?.records?.sort(compare(field, order)) : [...minuteList] })
+                        }
+                    }
+                }}
+                page={mode === 'total' ? queryForm.pageNum : undefined}
+                pageSize={mode === 'total' ? queryForm.pageSize : undefined}
+                scroll={{ y: 620 }}
+                config={mode === 'minute' ? qiliangpaihangminute : qiliangpaihang}
+                configName={mode === 'total' ? '起量广告排行' : mode === 'detail' ? '起量广告排行明细' : '起量广告5min'}
+            />
+        </div>
+
+        {visible && <PlanDetail visible={visible} onClose={() => { setVisible(false) }} data={aId} />}
+    </Space>
+}
+
+
+export default React.memo(Monitor)

+ 235 - 0
src/pages/adMonitor/adMonitorList/planList.tsx

@@ -0,0 +1,235 @@
+import { allPlanProps, downLoadAllAdListApi } from "@/services/adMonitor/adMonitor"
+import React, { useState, useEffect, useCallback } from "react"
+import { useModel } from "umi"
+import moment from "moment"
+import { downloadFile1, formatDate } from "@/utils/downloadFile"
+import { Button, Card, DatePicker, Input, Select, Space } from "antd"
+import PlanDetail from "./components/planDetail"
+import { CHUANGYIZHUANGTAI, GUANGGAOZHUANGTAI } from "./enum"
+import TableData from "@/pages/launchSystemNew/components/TableData";
+import { CloudDownloadOutlined } from "@ant-design/icons"
+import { guanggao } from "./config"
+import columnsPlanList from './tablePlanListConfig'
+
+
+const PlanList: React.FC = () => {
+    const { getAllPlanList, getBookListAll, getAdqAccountList } = useModel('useAdMonitor.useMonitor')
+    const { state } = useModel('useOperating.useWxGroupList')
+    // 变量开始
+    const [queryForm, setQueryForm] = useState<allPlanProps>({ pageNum: 1, pageSize: 20, createStartTime: moment().subtract(3, 'days').format('YYYY-MM-DD'), createEndTime: moment().format('YYYY-MM-DD') }) // 搜索变量
+    const [visible, setVisible] = useState<boolean>(false) // 详情弹窗控制
+    const [datas, setDatas] = useState<any[]>([])  // 所有数据
+    const [pichers, setPichers] = useState<any[]>([]) // 投手数据
+    const [adId, setAdId] = useState<any>()
+    const [downLoadLoading, setDownLoadLoading] = useState<boolean>(false)
+    // 变量结束
+
+    // 获取广告账号
+    useEffect(() => {
+        getAdqAccountList.run()
+    }, [])
+
+    useEffect(() => {
+        if (state?.allOfMember?.length > 0) {
+            setDatas(state?.allOfMember)
+        } else if (state?.myallOfUser?.length > 0) {
+            setDatas(state?.myallOfUser)
+        }
+    }, [state])
+
+    // 处理公众号
+    useEffect(() => {
+        if (datas.length > 0) {
+            let new_pichers = datas?.map((item: { key: { nickname: string, userId: number } }) => ({ nickName: item.key.nickname, userId: item.key.userId }))
+            if (JSON.stringify(pichers) !== JSON.stringify(new_pichers)) {//避免重复设置触发条件多次请求接口
+                setPichers(new_pichers)
+            }
+        }
+    }, [datas, queryForm?.sysUserId, pichers])
+
+    useEffect(() => {
+        if (sessionStorage.getItem('ADIDORNAME')) {
+            let data = JSON.parse(sessionStorage.getItem('ADIDORNAME') as any)
+            setQueryForm({ ...queryForm, adgroup: data.adgroupId, campaign: data.campaignId, sysUserId: data.putUserId, accountId: data?.accountId?.toString()?.split() })
+            sessionStorage.removeItem('ADIDORNAME')
+        }
+    }, [])
+
+    // 获取分组
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+
+    const getList = useCallback(() => {
+        getAllPlanList.run(queryForm)
+    }, [queryForm])
+
+    /** 获取书 */
+    useEffect(() => {
+        getBookListAll.run()
+    }, [])
+
+    // 设置创建时间
+    const setUnix = ([str, end]: string[]) => {
+        if (str && end) {
+            setQueryForm({ ...queryForm, pageNum: 1, createStartTime: moment(str).format('YYYY-MM-DD'), createEndTime: moment(end).format('YYYY-MM-DD') })
+        } else {
+            const { createStartTime, createEndTime, ...newQueyFor } = queryForm
+            setQueryForm({ ...newQueyFor, pageNum: 1 })
+        }
+    }
+
+    // 详情
+    const detail = (data: number) => {
+        setAdId(data)
+        setVisible(true)
+    }
+    // 接口自动刷新10分钟一次
+    useEffect(() => {
+        let time = setInterval(() => {
+            console.log('广告列表10分钟刷新')
+            getAllPlanList.refresh()
+        }, 1000 * 60 * 10)
+        return () => {
+            clearInterval(time)
+        }
+    }, [getAllPlanList])
+
+    // 下载
+    const downLoadExcel = useCallback(() => {
+        setDownLoadLoading(true)
+        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+        // let { page, pageSize, ...newQueryForm } = queryForm
+        Object.keys(newQueryForm).forEach((key: any) => {
+            if (key === 'mpAppIds') {
+                newQueryForm[key] = newQueryForm[key].join()
+            } else if (key === 'pitcherIds') {
+                newQueryForm[key] = newQueryForm[key]
+            }
+        })
+        if (!newQueryForm['pitcherIds'] && (!queryForm['mpAppIds'] || queryForm['mpAppIds']?.length === 0)) {
+            newQueryForm['pitcherIds'] = pichers?.map(item => item.userId)
+        }
+        downLoadAllAdListApi(newQueryForm).then(res => {
+            setDownLoadLoading(false)
+            downloadFile1(res, 'octet-stream', formatDate(new Date()) + ".xlsx")
+        }).catch(() => setDownLoadLoading(false))
+    }, [queryForm, pichers, downLoadLoading])
+
+    return <Space direction='vertical' style={{ width: '100%' }} className="planlist">
+        {/* 详情弹窗 */}
+        {visible && <PlanDetail visible={visible} onClose={() => { setVisible(false) }} data={adId} dataStartTime={queryForm?.dataStartTime} dataEndTime={queryForm?.dataEndTime} />}
+        <Card hoverable>
+            <Space direction='vertical' style={{ width: '100%' }}>
+                <Space wrap>
+                    <Select
+                        showSearch
+                        disabled={queryForm?.accountId?.length > 0}
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                        value={queryForm.sysUserId?.map((s: any) => s.toString())} mode='multiple' maxTagCount={1} style={{ minWidth: 150 }} allowClear placeholder="请选择投手" onChange={(value: any[]) => { setQueryForm({ ...queryForm, pageNum: 1, sysUserId: value }) }}>
+                        {pichers?.map((item: { nickName: string, userId: number }, index: number) => <Select.Option value={item.userId.toString()} key={item.userId + '' + index}>{item.nickName}</Select.Option>)}
+                    </Select>
+                    <Select
+                        showSearch
+                        mode='multiple'
+                        maxTagCount={1}
+                        value={queryForm.accountId}
+                        style={{ minWidth: 220 }}
+                        allowClear
+                        placeholder="请选择广告账号"
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                        onChange={(value: string) => {
+                            setQueryForm({ ...queryForm, accountId: value, pageNum: 1 })
+                        }}
+                    >
+                        {getAdqAccountList?.data?.data?.map((item: { id: number, accountId: number, wechatAccountName: string }) => <Select.Option
+                            value={item.accountId}
+                            key={item.id}
+                        >
+                            {item.accountId + "_" + item.wechatAccountName}
+                        </Select.Option>)}
+                    </Select>
+                    {/* <Select style={{ minWidth: 150 }} placeholder="请选择主投书" allowClear value={queryForm?.mainBookList} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, mainBookList: e }) }} mode="multiple" maxTagCount={1}>
+                        {getBookListAll?.data?.data?.map((item: { id: number, bookName: string }) => <Select.Option value={item.id} key={item?.id}>{item.bookName}</Select.Option>)}
+                    </Select> */}
+                    <Select style={{ width: 150 }} placeholder="请选择广告状态" value={queryForm.adStatus} onChange={(value: number) => {
+                        setQueryForm({ ...queryForm, pageNum: 1, adStatus: value })
+                    }} allowClear>
+                        {Object.keys(GUANGGAOZHUANGTAI).map(key => {
+                            return <Select.Option value={key} key={key}>{GUANGGAOZHUANGTAI[key]}</Select.Option>
+                        })}
+                    </Select>
+                    {/* <Select style={{ width: 150 }} placeholder="请选择创意状态" value={queryForm?.creativeStatus} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, creativeStatus: e }) }} allowClear>
+                        {
+                            Object.keys(CHUANGYIZHUANGTAI).map(key => {
+                                return <Select.Option value={key} key={key}>{CHUANGYIZHUANGTAI[key]}</Select.Option>
+                            })
+                        }
+                    </Select> */}
+                    {/* <Select style={{ width: 150 }} placeholder="请选择关注状态" value={queryForm?.followStatus} onChange={(e) => { setQueryForm({ ...queryForm, followStatus: e }) }} allowClear>
+                        <Select.Option value={1}>已关注</Select.Option>
+                        <Select.Option value={2}>未关注</Select.Option>
+                    </Select> */}
+
+                    <Input placeholder="请输入计划名称或ID" allowClear value={queryForm?.campaign} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, campaign: e.target.value }) }} />
+                    <Input placeholder="请输入广告名称或ID" allowClear value={queryForm?.adgroup} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, adgroup: e.target.value }) }} />
+                    {/* <Input placeholder="请输入创意名称或ID" allowClear value={queryForm?.creativeIdOrName} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, creativeIdOrName: e.target.value }) }} /> */}
+
+                </Space>
+                <Space wrap>
+                    <DatePicker.RangePicker placeholder={['数据开始日期', '数据结束日期']} onChange={(mo: any, str: string[]) => { setQueryForm({ ...queryForm, pageNum: 1, dataStartTime: str[0], dataEndTime: str[1] }) }} value={(queryForm?.dataStartTime && queryForm?.dataEndTime ? [moment(queryForm?.dataStartTime), moment(queryForm?.dataEndTime)] : null) as any} />
+                    <DatePicker.RangePicker placeholder={['创建开始日期', '创建结束日期']} onChange={(mo: any, str: string[]) => { setUnix(str) }} value={(queryForm?.createStartTime && queryForm?.createEndTime ? [moment(queryForm?.createStartTime), moment(queryForm?.createEndTime)] : null) as any} />
+                    {/* <DatePicker onChange={(e) => { e ? setQueryForm({ ...queryForm, dataTime: moment(e).format('YYYY-MM-DD') }) : setQueryForm({ ...queryForm, dataTime: '' }) }} value={queryForm?.dataTime ? moment(queryForm?.dataTime) : null} /> */}
+                    <Button type="primary" onClick={() => { getList() }}>搜索</Button>
+                </Space>
+            </Space>
+        </Card>
+
+        <TableData
+            columns={columnsPlanList(detail)}
+            ajax={getAllPlanList}
+            dataSource={getAllPlanList?.data?.data?.records}
+            loading={getAllPlanList?.loading}
+            scroll={{ y: 600 }}
+            total={getAllPlanList?.data?.data?.total}
+            page={queryForm.pageNum}
+            pageSize={queryForm.pageSize}
+            leftChild={<Button size="small" icon={<CloudDownloadOutlined />} loading={downLoadLoading} onClick={downLoadExcel}>下载</Button>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                newQueryForm.pageNum = current
+                newQueryForm.pageSize = pageSize
+                if (sortData && JSON.stringify('sortData') !== '{}') {
+                    let { field, order } = sortData   // descend 降序 大到小  ascend 升序 小到大
+                    if (order) {
+                        newQueryForm.sortField = field
+                        newQueryForm.sort = order === 'ascend' ? 'ASC' : 'DESC'
+                    } else {
+                        Object.keys(newQueryForm).forEach(key => {
+                            if (key === 'sortField' || key === 'sort') {
+                                delete newQueryForm[key]
+                            }
+                        })
+                    }
+                } else {
+                    Object.keys(newQueryForm).forEach(key => {
+                        if (key === 'sortField' || key === 'sort') {
+                            delete newQueryForm[key]
+                        }
+                    })
+                }
+                setQueryForm({ ...newQueryForm })
+            }}
+            config={guanggao}
+            configName={'广告列表'}
+        />
+    </Space>
+}
+
+export default React.memo(PlanList)

+ 27 - 0
src/pages/adMonitor/adMonitorList/table.less

@@ -0,0 +1,27 @@
+.MYtable {
+  tbody {
+    td {
+      padding: 0px !important;
+      .ant-progress-bg {
+        border-radius: 0 !important;
+        height: 100% !important;
+      }
+      .ant-progress-inner {
+        border-radius: 0 !important;
+        height: 100%;
+        background-color: #fff;
+      }
+      .ant-progress-line,.ant-progress-outer{
+          height: 100%;
+      }
+    }
+    tr:hover {
+      background-color: #d6d0d0 !important;
+    }
+    tr:active {
+      background-color: #d6d0d0 !important;
+    }
+  }
+}
+
+

+ 689 - 0
src/pages/adMonitor/adMonitorList/tableMonitorConfig.tsx

@@ -0,0 +1,689 @@
+import useCopy from '@/Hook/useCopy'
+import { DownOutlined, RiseOutlined } from '@ant-design/icons'
+import { Dropdown, Menu, Progress, Statistic, Tooltip} from 'antd'
+import { ColumnsType } from 'antd/lib/table'
+import React from 'react'
+import { ReactComponent as RocketSvg } from '@/assets/rocket.svg'
+import './index.less'
+import { CHUANGYIZHUANGTAI, GOUMAILEIXING, GUANGGAOZHUANGTAI, TUIGUANGMUBIAO, YOUHUAMUBIAO } from './enum'
+import Box from './components/box'
+function columnsMonitor(planDetail: (id: number) => void, getDetailList: (adId: any) => void, details: (id: number) => void, getMinuList: (id: number) => void, mode: string) {
+    const { copy } = useCopy()
+    return function columns() {
+        let newArr: ColumnsType<any> = [
+            {
+                title: '时间',
+                dataIndex: 'day',
+                key: 'day',
+                align: 'center',
+                width: 150,
+                render: (str, b) => {
+                    return str + ' ' + b.hour + ':00'
+                }
+            },
+            {
+                title: '广告名称',
+                dataIndex: 'adgroupName',
+                key: 'adgroupName',
+                align: 'center',
+                width: 170,
+                ellipsis: true,
+                render: (str, b) => {
+                    return <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { getDetailList(b.adgroupId) }}>{str}</a>
+                }
+            },
+            {
+                title: '广告ID',
+                dataIndex: 'adgroupId',
+                key: 'adgroupId',
+                align: 'center',
+                width: 100,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '计划名称',
+                dataIndex: 'campaignName',
+                key: 'campaignName',
+                align: 'center',
+                width: 170,
+                ellipsis: true,
+                render: (str, b) => {
+                    return str
+                }
+            },
+            {
+                title: '计划ID',
+                dataIndex: 'campaignId',
+                key: 'campaignId',
+                align: 'center',
+                width: 100,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '广告账户',
+                dataIndex: 'accountId',
+                key: 'accountId',
+                width: 70,
+                align: 'center'
+            },
+            {
+                title: '投手',
+                dataIndex: 'putUserName',
+                key: 'putUserName',
+                align: 'center',
+                width: 65
+            },
+            {
+                title: '创意预览',
+                dataIndex: 'creativePreview',
+                key: 'creativePreview',
+                width: 110,
+                align: 'center',
+                render: (a: any, b: any) => {
+                    return <Box b={b}/>
+                }
+            },
+            // {
+            //     title: '投放位置',
+            //     dataIndex: 'pitchSeat',
+            //     key: 'pitchSeat',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '自动扩量',
+            //     dataIndex: 'autoExpand',
+            //     key: 'autoExpand',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '深度优化目标',
+            //     dataIndex: 'depthOptimizeTarget',
+            //     key: 'depthOptimizeTarget',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '曝光评估',
+            //     dataIndex: 'impressionAppraise',
+            //     key: 'impressionAppraise',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            {
+                title: '投放时间',
+                dataIndex: 'adBeginTime',
+                key: 'adBeginTime',
+                width: 110,
+                align: 'center',
+                render: (a: any, b: any) => {
+                    return a
+                }
+            },
+            {
+                title: '广告状态',
+                dataIndex: 'adStatus',
+                key: 'adStatus',
+                align: 'center',
+                width: 105,
+                render: (a: any) => {
+                    return GUANGGAOZHUANGTAI[a] || '--'
+                }
+            },
+            // {
+            //     title: '创意状态',
+            //     dataIndex: 'adCreativeStatus',
+            //     key: 'adCreativeStatus',
+            //     align: 'center',
+            //     width: 115,
+            //     render: (a: any) => {
+            //         return CHUANGYIZHUANGTAI[a] || '--'
+            //     }
+            // },
+            {
+                title: '推广目标',
+                dataIndex: 'promotedObjectName',
+                key: 'promotedObjectName',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    return TUIGUANGMUBIAO[a]
+                }
+            },
+            // {
+            //     title: '购买类型',
+            //     dataIndex: 'adBuyType',
+            //     key: 'adBuyType',
+            //     align: 'center',
+            //     width: 110,
+            //     render: (a: any) => {
+            //         return GOUMAILEIXING[a] || '--'
+            //     }
+            // },
+            {
+                title: '广告预算',
+                dataIndex: 'dailyBudget',
+                key: 'dailyBudget',
+                width: 110,
+                align: 'center',
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            // {
+            //     title: '出价方式',
+            //     dataIndex: 'biddingMethod',
+            //     key: 'biddingMethod',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            {
+                title: '当前出价',
+                dataIndex: 'bidAmount',
+                key: 'bidAmount',
+                width: 110,
+                align: 'center',
+                render: (a: any) => {
+                    return <div style={a >= 500 ? { backgroundColor: 'rgba(255, 80, 82, .72)', height: 26, color: '#fff', display: 'flex', justifyContent: 'center', alignItems: 'center', fontWeight: 600 } : {}}><Statistic value={a || 0} valueStyle={a >= 500 ? { fontSize: 14 } : {}} /></div>
+                }
+            },
+            // {
+            //     title: '计划预算',
+            //     dataIndex: 'planBudget',
+            //     key: 'planBudget',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            {
+                title: '广告总消耗',
+                dataIndex: 'costTotal',
+                key: 'costTotal',
+                align: 'center',
+                width: 100,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#10c1e9',
+                                to: '#6892d0',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 30000 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 30000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '今日消耗',
+                dataIndex: 'costDay',
+                key: 'costDay',
+                align: 'center',
+                width: 105,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#ff5900',
+                                to: '#ffd380',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 2000 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 2000 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '当前小时消耗',
+                dataIndex: 'costHour',
+                key: 'costHour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#e7a0f5',
+                                to: '#d161f7',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 300 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 300 ? { color: '#000', fontWeight: 500, fontSize: 15 } : { fontWeight: 500, fontSize: 15 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '前第1小时消耗',
+                dataIndex: 'costLastHour',
+                key: 'costLastHour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '前第2小时消耗',
+                dataIndex: 'costLastTwoHour',
+                key: 'costLastTwoHour',
+                align: 'center',
+                width: 80,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '前第3小时消耗',
+                dataIndex: 'costLastThreeHour',
+                key: 'costLastThreeHour',
+                align: 'center',
+                width: 110,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '当前小时消耗差额',
+                dataIndex: 'costDiffBeforeHour',
+                key: 'costDiffBeforeHour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                }
+            },
+            {
+                title: '前第1小时消耗差额',
+                dataIndex: 'costDiffBeforeTwoHour',
+                key: 'costDiffBeforeTwoHour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                }
+            },
+            {
+                title: "前第2小时消耗差额",
+                dataIndex: 'costDiffBeforeThreeHour',
+                key: 'costDiffBeforeThreeHour',
+                align: 'center',
+                width: 125,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} valueStyle={a > 0 ? { color: 'red' } : { color: 'green' }} />
+                },
+            },
+            {
+                title: "前三小时消耗趋势",
+                dataIndex: 'costTrendLastThreeHour',
+                key: 'costTrendLastThreeHour',
+                align: 'center',
+                width: 75,
+                render: (a: number) => {
+                    return a > 0 ? <RiseOutlined style={{ color: 'red', fontWeight: 900, fontSize: 22 }} /> : '--'
+                },
+            },
+            {
+                title: '当前5min消耗流速',
+                dataIndex: 'costSpeed',
+                key: 'costSpeed',
+                align: 'center',
+                width: 115,
+                render: (a: any) => {
+                    a = a ? parseFloat(a.toFixed(2)) : 0
+                    return <div style={{ height: 26, position: 'relative' }}>
+                        <Progress
+                            strokeColor={{
+                                from: '#00DDFF',
+                                to: '#37A2FF',
+                            }}
+                            status="active"
+                            showInfo={false}
+                            percent={a ? a / 100 * 100 : 0}
+                        />
+                        <span style={{ position: 'absolute', left: 0, top: 2, bottom: 0, right: 0 }}><Statistic value={a || 0} valueStyle={a >= 100 ? { color: '#000', fontWeight: 500 } : { fontWeight: 500 }} /></span>
+                    </div>
+                }
+            },
+            {
+                title: '曝光量',
+                dataIndex: 'viewDay',
+                key: 'viewDay',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '千次曝光成本',
+                dataIndex: 'thousandDisplayPriceDay',
+                key: 'thousandDisplayPriceDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击量',
+                dataIndex: 'clickDay',
+                key: 'clickDay',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    return <Statistic value={a || 0} />
+                },
+            },
+            {
+                title: '点击均价',
+                dataIndex: 'cpcDay',
+                key: 'cpcDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '点击率',
+                dataIndex: 'ctrDay',
+                key: 'ctrDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '优化目标',
+                dataIndex: 'optimizationGoal',
+                key: 'optimizationGoal',
+                align: 'center',
+                width: 115,
+                render: (a: any) => {
+                    return YOUHUAMUBIAO[a] || '--'
+                }
+            },
+            {
+                title: '转化目标量',
+                dataIndex: 'conversionsCountDay',
+                key: 'conversionsCountDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化目标成本',
+                dataIndex: 'conversionsCostDay',
+                key: 'conversionsCostDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <div style={a > 500 ? { backgroundColor: '#efea5b', height: 26, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 600 } : {}}><Statistic value={a || 0} /></div>
+                }
+            },
+            {
+                title: '目标转化率',
+                dataIndex: 'conversionsRateDay',
+                key: 'conversionsRateDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单量',
+                dataIndex: 'orderCountDay',
+                key: 'orderCountDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单量',
+                dataIndex: 'firstDayOrderCountDay',
+                key: 'firstDayOrderCountDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单成本',
+                dataIndex: 'orderCostDay',
+                key: 'orderCostDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单率',
+                dataIndex: 'orderRateDay',
+                key: 'orderRateDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单金额',
+                dataIndex: 'orderAmountDay',
+                key: 'orderAmountDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单金额',
+                dataIndex: 'firstDayOrderAmountDay',
+                key: 'firstDayOrderAmountDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单客单价',
+                dataIndex: 'atvDay',
+                key: 'atvDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单ROI',
+                dataIndex: 'orderRoiDay',
+                key: 'orderRoiDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '首日新增下单ROI',
+                dataIndex: 'firstDayOrderRoiDay',
+                key: 'firstDayOrderRoiDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '公众号关注人数',
+                dataIndex: 'mpFollowUvDay',
+                key: 'mpFollowUvDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return a
+                }
+            },
+            {
+                title: '公众号关注率',
+                dataIndex: 'mpFollowRateDay',
+                key: 'mpFollowRateDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '公众号关注成本',
+                dataIndex: 'mpFollowCostDay',
+                key: 'mpFollowCostDay',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            // {
+            //     title: '注册人数',
+            //     dataIndex: 'mpRegisterUserCount',
+            //     key: 'mpRegisterUserCount',
+            //     align: 'center',
+            //     width: 115,
+            // sorter: true,
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '注册次数',
+            //     dataIndex: 'mpRegisterCount',
+            //     key: 'mpRegisterCount',
+            //     align: 'center',
+            //     width: 115,
+            // sorter: true,
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '注册成本',
+            //     dataIndex: 'mpRegisterCost',
+            //     key: 'mpRegisterCost',
+            //     align: 'center',
+            //     width: 115,
+            // sorter: true,
+            //     render: (a: any) => {
+            //         return <Statistic value={a || 0} />
+            //     }
+            // },
+            {
+                title: '操作',
+                dataIndex: 'event',
+                key: 'event',
+                align: 'center',
+                width: 90,
+                render: (a: string, b: any) => (
+                    // <a style={{ color: '#1890ff' }} onClick={() => { planDetail(b) }}>广告详情</a>
+                    <Dropdown overlay={
+                        <Menu>
+                            <Menu.Item key="0">
+                                <a style={{ color: '#1890ff' }} onClick={() => { planDetail(b) }}>广告详情</a>
+                            </Menu.Item>
+                            {mode === 'minute' ? <Menu.Item key="1">
+                                <a style={{ color: '#1890ff' }} onClick={() => { getDetailList(b.adgroupId) }}>小时</a>
+                            </Menu.Item> : <Menu.Item key="1">
+                                <a style={{ color: '#1890ff' }} onClick={() => { getMinuList(b?.adgroupId) }}>5min</a>
+                            </Menu.Item>}
+                            
+                            <Menu.Item key="2">
+                                <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }} onClick={() => details(b)}>
+                                    <RocketSvg /><a style={{ marginLeft: 4, color: '#1890ff' }}>详情</a>
+                                </div>
+                            </Menu.Item>
+                        </Menu>
+                    } trigger={['click']}>
+                        <a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
+                        更多 <DownOutlined />
+                        </a>
+                    </Dropdown>
+                )
+            }
+        ]
+        return newArr
+    }
+}
+export default columnsMonitor

+ 500 - 0
src/pages/adMonitor/adMonitorList/tablePlanListConfig.tsx

@@ -0,0 +1,500 @@
+import { Statistic, Tooltip } from 'antd'
+import React from 'react'
+import './index.less'
+import { ReactComponent as RocketSvg } from '@/assets/rocket.svg'
+import { GOUMAILEIXING, GUANGGAOZHUANGTAI, TUIGUANGMUBIAO, CHUANGYIZHUANGTAI, YOUHUAMUBIAO } from './enum'
+import Box from './components/box'
+function columnsMonitor(details: (id: number) => void) {
+    return function columns() {
+        let newArr: any = [
+            {
+                title: '数据日期',
+                dataIndex: 'beginToEnd',
+                key: 'beginToEnd',
+                align: 'center',
+                width: 100,
+                fixed: 'left'
+            },
+            {
+                title: '广告名称/ID',
+                dataIndex: 'adgroupName',
+                key: 'adgroupName',
+                align: 'left',
+                width: 210,
+                ellipsis: true,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', fontSize: 12 }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }}>{b.adgroupName}</div>
+                        <div style={{ color: '#888' }}>广告ID:{b?.adGroupId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '投放计划名称/ID',
+                dataIndex: 'campaignName/campaignId',
+                key: 'campaignName/campaignId',
+                align: 'left',
+                width: 180,
+                ellipsis: true,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', fontSize: 12 }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }}>{b.campaignName}</div>
+                        <div style={{ color: '#888' }}>计划ID:{b?.campaignId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '创意名称/ID',
+                dataIndex: 'creativeName/creativeId',
+                key: 'creativeName/creativeId',
+                align: 'left',
+                width: 210,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', fontSize: 12 }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }}>{b.creativeName}</div>
+                        <div style={{ color: '#888' }}>创意ID:{b?.creativeId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '创意预览',
+                dataIndex: 'creativePreview',
+                key: 'creativePreview',
+                width: 110,
+                align: 'center',
+                render: (a: any, b: any) => {
+                    return <Box b={b} />
+                }
+            },
+            {
+                title: '投手',
+                dataIndex: 'putUserName',
+                key: 'putUserName',
+                width: 70,
+                align: 'center',
+                render: (a: any) => {
+                    return a || '--'
+                }
+            },
+            {
+                title: '创建日期',
+                dataIndex: 'adCreateTime',
+                key: 'adCreateTime',
+                align: 'center',
+                width: 110
+            },
+            {
+                title: '广告状态',
+                dataIndex: 'adStatus',
+                key: 'adStatus',
+                align: 'center',
+                width: 105,
+                render: (a: any) => {
+                    return GUANGGAOZHUANGTAI[a] || '--'
+                }
+            },
+            // {
+            //     title: '创意状态',
+            //     dataIndex: "adCreativeStatus",
+            //     key: "adCreativeStatus",
+            //     align: 'center',
+            //     width: 105,
+            //     render: (a: any) => {
+            //         return CHUANGYIZHUANGTAI[a] || '--'
+            //     }
+            // },
+            // {
+            //     title: '购买类型',
+            //     dataIndex: 'adBuyType',
+            //     key: 'adBuyType',
+            //     align: 'center',
+            //     width: 110,
+            //     render: (a: any) => {
+            //         return GOUMAILEIXING[a] || '--'
+            //     }
+            // },
+            // {
+            //     title: '自动扩量',
+            //     dataIndex: 'autoExpand',
+            //     key: 'autoExpand',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            // {
+            //     title: '曝光评估',
+            //     dataIndex: 'impressionAppraise',
+            //     key: 'impressionAppraise',
+            //     align: 'center',
+            //     width: 115,
+            //     render: (a: any) => {
+            //         return a || '--'
+            //     }
+            // },
+            {
+                title: '投放时间',
+                dataIndex: 'adBeginTime',
+                key: 'adBeginTime',
+                align: 'center',
+                width: 120,
+                render: (a: any, b: any) => {
+                    return a ? <div style={{ display: 'flex', flexFlow: 'column nowrap' }}>
+                        <span>{a}</span>
+                        {/* <span>{b?.adEndDate}</span> */}
+                    </div> : '--'
+                }
+            },
+            // {
+            //     title: '当日成本偏差',
+            //     dataIndex: 'dayCostOffset',
+            //     key: 'dayCostOffset',
+            //     align: 'center',
+            //     width: 90,
+            //     render: (a: any) => {
+            //         return a || '--'
+            //     }
+            // },
+            {
+                title: '推广目标',
+                dataIndex: 'promotedObjectName',
+                key: 'promotedObjectName',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    // return TUIGUANGMUBIAO[a] || '推广公众号'
+                    return a
+                }
+            },
+            // {
+            //     title: '深度优化目标',
+            //     dataIndex: 'depthOptimizeTarget',
+            //     key: 'depthOptimizeTarget',
+            //     width: 110,
+            //     align: 'center',
+            //     render: (a: any) => {
+            //         return a
+            //     }
+            // },
+            {
+                title: '广告预算',
+                dataIndex: 'adBudget',
+                key: 'adBudget',
+                align: 'center',
+                width: 110,
+                render: (a: any) => {
+                    return a ? <Statistic value={a | 0} /> : '--'
+                }
+            },
+            {
+                title: '出价方式',
+                dataIndex: 'bidMode',
+                key: 'bidMode',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    return a || '--'
+                }
+            },
+            {
+                title: '当前出价',
+                dataIndex: 'bidAmount',
+                key: 'bidAmount',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    return a ? <Statistic value={a | 0} /> : '--'
+                }
+            },
+            // {
+            //     title: '计划预算',
+            //     dataIndex: 'planBudget',
+            //     key: 'planBudget',
+            //     align: 'center',
+            //     width: 80,
+            //     render: (a: any) => {
+            //         return a ? <Statistic value={a | 0} /> : '--'
+            //     }
+            // },
+            {
+                title: '广告总消耗',
+                dataIndex: 'costTotal',
+                key: 'costTotal',
+                align: 'center',
+                width: 100,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            // {
+            //     title: '今日消耗',
+            //     dataIndex: 'todayCostTotal',
+            //     key: 'todayCostTotal',
+            //     align: 'center',
+            //     width: 105,
+            //     render: (a: any) => {
+            //         return <Statistic value={a || 0} />
+            //     }
+            // },
+            {
+                title: '单位时间消耗速度',
+                dataIndex: 'costSpeed',
+                key: 'costSpeed',
+                align: 'center',
+                width: 120,
+                render: (a: any, b: any) => {
+                    return <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+                        {/* <div>当前:<span></span></div>
+                        <div>前第一小时:<span></span></div> */}
+                        <RocketSvg /> <a onClick={() => details(b)} style={{ marginLeft: 10 }}>详情</a>
+                    </div>
+                }
+            },
+            {
+                title: '曝光量',
+                dataIndex: 'viewCount',
+                key: 'viewCount',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    // a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return <span style={a <= 8 ? { color: '#0f990f', fontWeight: 600 } : a >= 100 ? { color: 'red', fontWeight: 600 } : {}}> {a || '--'}</span >
+                },
+            },
+            {
+                title: '千次曝光成本',
+                dataIndex: 'thousandDisplayPriceTotal',
+                key: 'thousandDisplayPriceTotal',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            },
+            {
+                title: '点击量',
+                dataIndex: 'clickCount',
+                key: 'clickCount',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    // a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return <span style={a <= 8 ? { color: '#0f990f', fontWeight: 600 } : a >= 100 ? { color: 'red', fontWeight: 600 } : {}}> {a || '--'}</span >
+                },
+            },
+            {
+                title: '点击均价',
+                dataIndex: 'avgClickAmount',
+                key: 'avgClickAmount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            },
+            {
+                title: '点击率',
+                dataIndex: 'clickRate',
+                key: 'clickRate',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '优化目标',
+                dataIndex: 'optimizationGoal',
+                key: 'optimizationGoal',
+                align: 'center',
+                width: 115,
+                render: (a: any) => {
+                    return <Tooltip title={YOUHUAMUBIAO[a]}>
+                        <div className='oneText'>{YOUHUAMUBIAO[a] || '--'}</div>
+                    </Tooltip>
+                }
+            },
+            {
+                title: '转化目标量',
+                dataIndex: 'conversionsCount',
+                key: 'conversionsCount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '转化目标成本',
+                dataIndex: 'conversionsCost',
+                key: 'conversionsCost',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            },
+            {
+                title: '目标转化率',
+                dataIndex: 'conversionsRate',
+                key: 'conversionsRate',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单量',
+                dataIndex: 'orderCount',
+                key: 'orderCount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单量',
+                dataIndex: 'firstDayOrderCountTotal',
+                key: 'firstDayOrderCountTotal',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单成本',
+                dataIndex: 'orderCost',
+                key: 'orderCost',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            },
+            {
+                title: '下单率',
+                dataIndex: 'orderRate',
+                key: 'orderRate',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '下单金额',
+                dataIndex: 'orderAmount',
+                key: 'orderAmount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单金额',
+                dataIndex: 'firstDayOrderAmountTotal',
+                key: 'firstDayOrderAmountTotal',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单客单价',
+                dataIndex: 'atvAmount',
+                key: 'atvAmount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            },
+            {
+                title: '下单ROI',
+                dataIndex: 'orderROI',
+                key: 'orderROI',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '首日新增下单ROI',
+                dataIndex: 'firstDayOrderRoiTotal',
+                key: 'firstDayOrderRoiTotal',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '公众号关注人数',
+                dataIndex: 'mpFollowUser',
+                key: 'mpFollowUser',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return a || '--'
+                }
+            },
+            {
+                title: '公众号关注率',
+                dataIndex: 'mpFollowRate',
+                key: 'mpFollowRate',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    a = a ? parseFloat((a * 100).toFixed(2)) : 0
+                    return a + '%'
+                }
+            },
+            {
+                title: '公众号关注成本',
+                dataIndex: 'mpFollowCost',
+                key: 'mpFollowCost',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a ? a?.toFixed(2) : 0} />
+                }
+            }
+        ]
+        return newArr
+    }
+}
+export default columnsMonitor
+

+ 10 - 6
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -40,10 +40,14 @@ interface Prosp {
     hoverable?: boolean,
     rowSelection?: any,
     myKey?: any,//自定义使用哪个值做key
+    fixed?: {
+        left: number,
+        right: number
+    },
 }
 
 function TableData(props: Prosp) {
-    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, leftChild, page = undefined, rowSelection = false, pageSize = undefined, size = 'small', total = 0, loading = false, onChange, config, configName, ajax, syncAjax, hoverable = true, myKey } = props
+    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, leftChild, page = undefined, rowSelection = false, pageSize = undefined, size = 'small', fixed = { left: 0, right: 1 }, total = 0, loading = false, onChange, config, configName, ajax, syncAjax, hoverable = true, myKey } = props
     const { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -51,7 +55,7 @@ function TableData(props: Prosp) {
     const [oldFixed, setoldFixed] = useState<any>({ left: '0', right: '0' })
     const [newColumns, setNewColumns] = useState<any[]>([])
     // const [oldConfigName, setOldConfigName] = useState<any>('')//上次的配置名称
-    const [selectData, setSelectData] = useState<{ selectData: any[], fixed: { left: string, right: string } }>({ selectData: [], fixed: { left: '0', right: '0' } })
+    const [selectData, setSelectData] = useState<{ selectData: any[], fixed: { left: number, right: number } }>({ selectData: [], fixed: { left: fixed.left, right: fixed.right } })
     const [tiptopShow, setTipTopShow] = useState({//tiptop开关
         ajaxShow: false,
         syncAjaxShow: false,
@@ -72,7 +76,7 @@ function TableData(props: Prosp) {
                 console.log('首次使用个人配置赋值')
                 let newDefSelectData = JSON.parse(defSelectData)
                 newDefSelectData = newDefSelectData.filter((item: any) => !!item && newConfig.some((c: { dataIndex: any }) => c.dataIndex === item.dataIndex))//去除空项
-                setSelectData(() => ({ selectData: newDefSelectData, fixed: defFixed ? JSON.parse(defFixed) : { left: '0', right: '0' } }))
+                setSelectData(() => ({ selectData: newDefSelectData, fixed: defFixed ? JSON.parse(defFixed) : { left: 0, right: 0 } }))
             }
             if (!defSelectData && (selectData?.selectData?.length === 0 || configName !== oldConfigName)) {//首次查找个人配置是否存在,并且selectData为空,不存在默认配置设置selectData
                 let newSelectData: any[] = []
@@ -84,7 +88,7 @@ function TableData(props: Prosp) {
                     })
                 })
                 console.log('首次使用默认配置赋值')
-                setSelectData(() => ({ selectData: newSelectData, fixed: { left: '0', right: configName.includes('起量') ? '2' : '0' } }))
+                setSelectData(() => ({ ...selectData, selectData: newSelectData }))
             }
             if ((JSON.stringify(oldSelectData) !== JSON.stringify(selectData?.selectData)) || (JSON.stringify(selectData.fixed) !== JSON.stringify(oldFixed))) {
                 console.log('设置配置改变重新赋值')
@@ -210,7 +214,7 @@ function TableData(props: Prosp) {
                         onClick={() => {
                             setVisible(true)
                         }}>
-                        <Tooltip title='设置'><SettingOutlined style={{ fontSize: 17 }} /></Tooltip>
+                        <Tooltip title='设置'><SettingOutlined /></Tooltip>
                     </Button>}
                     <Button
                         type='text'
@@ -225,7 +229,7 @@ function TableData(props: Prosp) {
                             <Tooltip title={!isFell ? '全屏' : '退出全屏'}>{!isFell ? <FullscreenOutlined /> : <FullscreenExitOutlined />}</Tooltip>
                         }
                     </Button>
-                    {visible && <CustomListModel version={version} config={config} configName={configName} visible={visible} onClose={() => { setVisible(false) }} onChange={(arr: any) => { setSelectData(arr) }} columns={newColumns?.length > 0 ? newColumns : columns()} />}
+                    {visible && <CustomListModel sysFixed={fixed} version={version} config={config} configName={configName} visible={visible} onClose={() => { setVisible(false) }} onChange={(arr: any) => { if (arr) { setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } }) } else { setSelectData({ selectData: [], fixed: { left: fixed.left, right: fixed.right } }) } }} columns={newColumns} />}
                 </div>
             </Col>
         </Row>

+ 258 - 0
src/services/adMonitor/adMonitor.ts

@@ -0,0 +1,258 @@
+import { api } from '../api'
+import { request } from 'umi'
+
+export interface ListType {
+    // startTime?: string,
+    // endTime?: string,
+    timeUnit?: 'minute' | 'hour' | 'day',//时间维度
+    adgroup?: any,//广告ID
+    campaign?: any,//计划ID
+    accountId?: any,//广告账户
+    sysUserId?: any,//投手
+    pageNum?: number,//页数
+    pageSize?: number//条数
+    sortField?: string, // "排序字段"
+    sort?: 'ASC' | 'DESC'  // 排序方式
+}
+
+/** 获取起量广告列表总表 */
+export async function getPlanListApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/up/ad/list`, {
+        method: 'POST',
+        data: params
+    })
+}
+/** 获取起量广告列表明细 */
+export async function getDetailListApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/up/ad/detail/list`, {
+        method: 'POST',
+        data: params
+    })
+}
+
+/** 获取起量广告列表5min */
+export async function getMinuteListApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/up/ad/detail/list/minute`, {
+        method: 'POST',
+        data: params
+    })
+}
+
+/** 获取今日计划总消耗图谱 折线图 */
+export async function getTotalCostApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/total/cost/list`, {
+        method: 'GET',
+        params
+    })
+}
+
+/** 获取计划消耗图谱 z柱状图 */
+export async function getPlanCostApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/ad/cost/list`, {
+        method: 'GET',
+        params
+    })
+}
+
+/** 获取消耗速度详情(弹窗) */
+export async function getCostSpeedApi(params: ListType) {
+    Object.keys(params).forEach(key => {
+        if (!params[key]) {
+            delete params[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/cost/speed/info`, {
+        method: 'POST',
+        data: params
+    })
+}
+
+/** 获取组员 */
+export async function getUserGroupApi() {
+    return request(`${api}/erp/userGroup/memberUser/`, {
+        method: 'GET'
+    })
+}
+
+
+export interface allPlanProps {
+    pageNum?: number,
+    pageSize?: number,
+    adSeat?: any[],// 广告位置 1 : 朋友圈信息流, 2 : 公众号文章底部, 3 : 公众号文中广告, 4 : 公众号文章视频贴片, 5 : 小程序banner广告, 6 : 激励式广告
+    createStartTime?: string, // 创建开始时间
+    createEndTime?: string,  // 创建结束时间
+    adgroup?: string,  // 广告ID & 广告名称
+    creativeIdOrName?: string,  // 创意ID & 创意名称
+    creativeStatus?: 1 | 2 | 3,  // 创意状态, 0或者null : 全部, 1 : 审核通过, 2 : 审核中, 3 : 未通过
+    dataStartTime?: string,  // 数据结束日期(格式 : yyyy-MM-dd)
+    dataEndTime?: string,  // 数据结束日期(格式 : yyyy-MM-dd)
+    followStatus?: 1 | 2,  // 关注状态, 0或者null : 全部, 1 : 已关注, 2 : 未关注
+    mainBookList?: any[],   // 主投书籍列表
+    accountId?: any,  // 投放计划数据源, 账号
+    sysUserId?: any,  // 投手id列表
+    campaign?: string, // 计划id或者计划名称
+    adStatus?: number, // 计划状态, 0或者null : 全部, 1 : 投放中, …依次递增
+    sortField?: string, // "排序字段"
+    sort?: 'ASC' | 'DESC',  // 排序方式
+}
+/** 计划列表 */
+export async function getAllPlanListApi(data: allPlanProps) {
+    Object.keys(data).forEach(key => {
+        if (!data[key]) {
+            delete data[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adControl/all/ad/list`, {
+        method: 'POST',
+        data
+    })
+}
+
+
+/** 获取所有书籍 */
+export async function getBookListAllApi() {
+    return request(`${api}/erp/book/all`, {
+        method: 'GET'
+    })
+}
+
+/** 下载 */
+/** 下载起量广告总表 */
+export async function downLoadUpAdApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/up/ad/list`, {
+        method: 'GET',
+        params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载单个广告明细表(小时) */
+export async function downLoadDetailApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/up/ad/detail/list`, {
+        method: 'GET',
+        params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载单个广告明细表(5分钟) */
+export async function downLoadDetailMinuteApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/up/ad/detail/list/minute`, {
+        method: 'GET',
+        params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载消耗速度详情(弹窗) */
+export async function downLoadSpeedApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/cost/speed/info`, {
+        method: 'GET',
+        params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载计划列表 */
+export async function downLoadAllAdListApi(data: allPlanProps) {
+    Object.keys(data).forEach(key => {
+        if (!data[key]) {
+            delete data[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adExcel/all/ad/list`, {
+        method: 'POST',
+        data,
+        responseType: 'blob'
+    })
+}
+
+
+// 监控数据源
+// 查询分组
+export async function getAdGroupListApi() {
+    return request(`${api}/tencentMonitor/adGroup/list`, {
+        method: 'GET'
+    })
+}
+
+// 查询分组
+export async function addEditGroupApi(data: { groupName: string, id?: number, remark?: string }) {
+    return request(`${api}/tencentMonitor/adGroup/add/or/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+// 删除分组
+export async function deleteAdGroupApi(params: { id: number }) {
+    return request(`${api}/tencentMonitor/adGroup/delete`, {
+        method: 'DELETE',
+        params
+    })
+}
+
+// 分组账号查询
+export interface AccountListProps {
+    // beginDate?: string,
+    // endDate?: string,
+    groupId?: number,
+    mpAppIdList?: string[],
+    pitcherIdList?: string[],
+    pageNum: number,
+    pageSize: number
+}
+export async function getAccountListApi(data: AccountListProps) {
+    Object.keys(data).forEach(key => {
+        if (!data[key]) {
+            delete data[key]
+        }
+    })
+    return request(`${api}/tencentMonitor/adGroup/accountList`, {
+        method: 'POST',
+        data
+    })
+}
+
+// 账号绑定或者解绑分组
+export async function addDelAccountApi(data: { accountIdList: number[], groupId: number, type: 0 | 1 }) {
+    return request(`${api}/tencentMonitor/adGroup/account/update`, {
+        method: 'POST',
+        data
+    })
+}
+
+/**
+ * 获取广告账户
+ * @returns 
+ */
+export async function getAdqAccountListApi() {
+    return request(`${api}/adq/adAccount/allOfUser`, {
+        method: 'Get'
+    })
+}

+ 1 - 1
src/services/user.ts

@@ -16,7 +16,7 @@ export async function queryCurrent() {
 export async function getMenu(): Promise<any> {
   return request(api + '/erp/menu/getRouters', {
     method: 'PUT',
-    data: ["adq_put"]
+    data: ["adq_put", "ad_monitor"]
   })
 }