shenwu hace 2 años
padre
commit
75b02b9d40
Se han modificado 72 ficheros con 7204 adiciones y 1835 borrados
  1. 2 1
      config/config.ts
  2. 2 2
      config/proxy.ts
  3. 23 4
      config/routerConfig.ts
  4. 2 2
      src/Hook/useAjax.tsx
  5. 6 6
      src/Hook/useEcharts.tsx
  6. 116 37
      src/components/CustomList/index.tsx
  7. 3 0
      src/components/FileBoxAD/components/imgModal/index.tsx
  8. 30 0
      src/components/FileBoxAD/components/imgsModal/index.less
  9. 132 0
      src/components/FileBoxAD/components/imgsModal/index.tsx
  10. 9 5
      src/components/FileBoxAD/index.tsx
  11. 65 0
      src/models/useAdMonitor/useMonitor.ts
  12. 115 11
      src/models/useLaunchAdq/useBdMedia.ts
  13. 116 14
      src/models/useLaunchAdq/useBdMediaPup.ts
  14. 6 2
      src/models/useOperating/useWxGroupList.ts
  15. 30 0
      src/pages/adMonitor/adMonitorList/components/box.tsx
  16. 184 0
      src/pages/adMonitor/adMonitorList/components/planDetail.tsx
  17. 73 0
      src/pages/adMonitor/adMonitorList/components/tableConfig.tsx
  18. 376 0
      src/pages/adMonitor/adMonitorList/config.ts
  19. 19 0
      src/pages/adMonitor/adMonitorList/data.tsx
  20. 129 0
      src/pages/adMonitor/adMonitorList/enum.ts
  21. 114 0
      src/pages/adMonitor/adMonitorList/index.less
  22. 31 0
      src/pages/adMonitor/adMonitorList/index.tsx
  23. 474 0
      src/pages/adMonitor/adMonitorList/monitor.tsx
  24. 223 0
      src/pages/adMonitor/adMonitorList/planList.tsx
  25. 27 0
      src/pages/adMonitor/adMonitorList/table.less
  26. 698 0
      src/pages/adMonitor/adMonitorList/tableMonitorConfig.tsx
  27. 554 0
      src/pages/adMonitor/adMonitorList/tablePlanListConfig.tsx
  28. 2 3
      src/pages/launchSystemNew/adq/index.tsx
  29. 15 8
      src/pages/launchSystemNew/components/TableData/index.tsx
  30. 43 765
      src/pages/launchSystemNew/components/addLandingPage/content.ts
  31. 185 415
      src/pages/launchSystemNew/components/addLandingPage/index.tsx
  32. 1 1
      src/pages/launchSystemNew/components/addLandingPage/index1.less
  33. 2 2
      src/pages/launchSystemNew/components/addLandingPage/landingPageReducer.ts
  34. 55 61
      src/pages/launchSystemNew/components/addLandingPage/sortable.tsx
  35. 6 1
      src/pages/launchSystemNew/components/customerServiceModal/index.tsx
  36. 3 2
      src/pages/launchSystemNew/components/dataSourceModal/index.tsx
  37. 10 2
      src/pages/launchSystemNew/components/goodsModal/index.tsx
  38. 1 1
      src/pages/launchSystemNew/components/previewOrigin/index.tsx
  39. 19 16
      src/pages/launchSystemNew/components/selectCloud/index.tsx
  40. 1 1
      src/pages/launchSystemNew/components/textAideInput/index.tsx
  41. 46 5
      src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx
  42. 62 4
      src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx
  43. 3 3
      src/pages/launchSystemNew/launchManage/createAd/creative/index.tsx
  44. 53 41
      src/pages/launchSystemNew/launchManage/createAd/creative/modal/index.tsx
  45. 168 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/index.tsx
  46. 196 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/brandImage.tsx
  47. 80 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/config.ts
  48. 89 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/copywriting.tsx
  49. 150 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/headNickJump.tsx
  50. 117 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.less
  51. 894 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.tsx
  52. 434 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/material.tsx
  53. 152 0
      src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/tableConfig.tsx
  54. 67 3
      src/pages/launchSystemNew/launchManage/createAd/index.less
  55. 382 103
      src/pages/launchSystemNew/launchManage/createAd/index.tsx
  56. 10 2
      src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx
  57. 10 3
      src/pages/launchSystemNew/launchManage/createAd/targeting/index.tsx
  58. 11 7
      src/pages/launchSystemNew/launchManage/createAd/targeting/modal/index.tsx
  59. 6 1
      src/pages/launchSystemNew/launchManage/taskList/index.tsx
  60. 13 12
      src/pages/launchSystemNew/launchManage/taskList/logTableConfig.tsx
  61. 3 2
      src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx
  62. 1 1
      src/pages/launchSystemNew/material/cloud/index.tsx
  63. 46 278
      src/pages/launchSystemNew/req.ts
  64. 258 0
      src/services/adMonitor/adMonitor.ts
  65. 2 2
      src/services/api.ts
  66. 5 1
      src/services/launchAdq/createAd.ts
  67. 0 0
      src/services/launchAdq/enum.ts
  68. 11 3
      src/services/launchAdq/material.ts
  69. 5 1
      src/services/launchAdq/taskList.ts
  70. 4 0
      src/services/operating/account.ts
  71. 1 1
      src/services/user.ts
  72. 23 0
      src/utils/utils.ts

+ 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: '/',

+ 2 - 2
config/proxy.ts

@@ -10,8 +10,8 @@
  export default {
   dev: {
     '/api/': {
-      target: 'http://test.api.zanxiangwl.com/api',
-      // target: 'http://api.zanxiangwl.com',
+      // target: 'http://test.api.zanxiangwl.com',
+      target: 'http://api.zanxiangwl.com',
       changeOrigin: true,
       pathRewrite: { '/api': '' },
     },

+ 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/useAjax.tsx

@@ -46,8 +46,8 @@ export function useAjax(fnc: CombineService<any, any>, options?: Options) {
         manual: options?.manual || true,
         cacheKey: options?.cacheKey,
         pollingInterval: options?.pollingInterval || undefined,
-        debounceInterval: options?.debounceInterval || undefined,
-        // throttleInterval:500,
+        debounceInterval: options?.debounceInterval || 0,
+        throttleInterval: options?.throttleInterval || 0,
         formatResult: (res) => {
             let reqTime = moment().format('YYYY-MM-DD HH:mm:ss')
             res['reqTime'] = reqTime

+ 6 - 6
src/Hook/useEcharts.tsx

@@ -490,7 +490,7 @@ function BarMonitor(props: {
     xName?: string,
     yName?: string,
     planID?: string,
-    onChange?: (id?: string) => void,
+    onChange?: (id: string, accountId: number[]) => void,
 }) {
     const { data, style, fontColor, title, xName, yName, planID, onChange } = props
     const colors = ['#f8128d', '#fa40a3', '#f56db5', '#f58bc4', '#f7a1cf', '#f5b7d8', '#facee5', '#f8c8e2', '#f8d6e8', '#fae8f1']
@@ -586,8 +586,8 @@ function BarMonitor(props: {
                         color: '#3946c3',
                         fontSize: 16
                     },
-                    left: '45%',
-                    top: 330,
+                    left: 'center',
+                    bottom: 0,
                 },
                 tooltip: {
                     show: true,
@@ -619,7 +619,7 @@ function BarMonitor(props: {
             myChart.on('click', (params: any) => {
                 let v = params.dataIndex
                 let d: any = (data as any[])[v]
-                onChange && onChange(d.name)
+                onChange && onChange(d.name, [d.accountId])
             });
         }
     }, [ref?.current, data, planID])
@@ -752,8 +752,8 @@ function LineMonitor(props: {
                 },
                 title: {
                     text: title || '',
-                    left: '45%',
-                    top: 330,
+                    left: 'center',
+                    bottom: 0,
                     textStyle: {
                         color: '#3946c3',
                         fontSize: 16

+ 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} />)}

+ 3 - 0
src/components/FileBoxAD/components/imgModal/index.tsx

@@ -110,6 +110,9 @@ let ImgModal = React.memo((props: { isAll?: boolean }) => {
                                     setQueryForm({ ...queryForm, file: newFileList.file })
                                     setVisible(true)
                                 }}
+                                onRemove={() => {
+                                    setQueryForm({ ...queryForm, file: null })
+                                }}
                                 onPreview={(file: any) => {
                                     setPreviewVisible(true)
                                     setFileUrl(file.thumbUrl)

+ 30 - 0
src/components/FileBoxAD/components/imgsModal/index.less

@@ -0,0 +1,30 @@
+
+
+.imgsModal {
+
+    
+}
+.previewGroup {
+    position: relative;
+    .clear {
+        position: absolute;
+        right: -5px;
+        top: -8px;
+        color: red;
+        z-index: 20;
+        cursor: pointer;
+    }
+    .ant-image {
+        border: 1px solid rgb(210, 210, 210);
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        box-sizing: border-box;
+        overflow: hidden;
+        .ant-image-img {
+            width: auto !important;
+            display: inline-block;
+            box-sizing: border-box;
+        }
+    }
+}

+ 132 - 0
src/components/FileBoxAD/components/imgsModal/index.tsx

@@ -0,0 +1,132 @@
+import React, { useState } from 'react'
+import { Form, Input, InputNumber, message, Modal, Space, Upload, Image } from 'antd'
+import { useModel } from 'umi'
+import { RcFile } from 'antd/lib/upload'
+import CropperImg from '../Cropper'
+import { CloseCircleFilled} from '@ant-design/icons'
+import './index.less'
+
+/**新建非图文素材 */
+let ImgsModal = React.memo((props: { isAll?: boolean }) => {
+
+    /*****========STA=========*****/
+    const { isAll } = props
+    const [queryForm, setQueryForm] = useState<{ title: string, sort: number, fileSize: number, files: any[] }>({ title: '', sort: 0, fileSize: 0, files: [] })
+    const [visible, setVisible] = useState<boolean>(false)
+    const [file, setFile] = useState<any>(null)
+    const { state, typeEnum, addFiles, offEditFile } = useModel(isAll ? 'useLaunchAdq.useBdMedia' : 'useLaunchAdq.useBdMediaPup')
+    const { imgsVisrible, mediaType, actionItem, upLoadLoading } = state
+    /*****========END=========*****/
+
+
+    const handleOk = (e: any) => {
+        e?.stopPropagation()
+        if (queryForm?.files?.length > 0) {
+            addFiles({ ...queryForm });
+        } else {
+            message.error('请上传素材')
+        }
+    }
+
+    const clearHandle = (index: number) => {
+        setQueryForm(() => ({ ...queryForm, files: queryForm.files.filter((item: any, i) => i !== index) }))
+    }
+
+    return <div>
+        <Modal
+            title={(actionItem ? '编辑' : '批量新建') + typeEnum[mediaType as string]}
+            visible={imgsVisrible}
+            onOk={(e) => { handleOk(e) }}
+            confirmLoading={upLoadLoading}
+            onCancel={() => {
+                offEditFile();
+            }}
+            width={450}
+            destroyOnClose
+            maskClosable={false}
+        >
+            <Form
+                name="basic"
+                labelCol={{ span: 5 }}
+                wrapperCol={{ span: 19 }}
+                autoComplete="off"
+            >
+                <Form.Item
+                    label="图片名称"
+                >
+                    <Input value={queryForm.title} onChange={(e) => setQueryForm({ ...queryForm, title: e.target.value })} placeholder="请输入图片名称" />
+                </Form.Item>
+                {mediaType === 'IMG' && <Form.Item
+                    label="图片大小"
+                    tooltip="单位KB,0代表不填默认图片300KB;图片太大会压缩到此大小下"
+                >
+                    <InputNumber value={queryForm.fileSize} onChange={(e) => setQueryForm({ ...queryForm, fileSize: e })} placeholder="请输入图片大小" />
+                </Form.Item>}
+                <Form.Item
+                    label="排序"
+                    tooltip="数值越大越靠前"
+                >
+                    <InputNumber value={queryForm.sort} onChange={(e) => setQueryForm({ ...queryForm, sort: e })} placeholder="请输入排序" />
+                </Form.Item>
+                <Form.Item
+                    label={'上传' + typeEnum[mediaType as string]}
+                >
+                    <Space direction='vertical'>
+                        <Image.PreviewGroup>
+                            <Space wrap>
+                                {queryForm?.files?.map((item: any, index: number) => <div key={index} className='previewGroup'>
+                                    <div className='clear' onClick={() => clearHandle(index)}><CloseCircleFilled /></div>
+                                    <Image height={60} width={60} src={URL.createObjectURL(item)} className='imagePre'/>
+                                </div>)}
+                            </Space>
+                        </Image.PreviewGroup>
+                        <Space>
+                            <Upload
+                                listType="picture-card"
+                                accept='image/gif,image/jpeg,image/png,image/jpg'
+                                beforeUpload={(file: RcFile) => {
+                                    return false
+                                }}
+                                multiple
+                                fileList={[]}
+                                onChange={(newFileList: any) => {
+                                    console.log('newFileList-->', newFileList)
+                                    let newFiles: any[] = queryForm.files
+                                    newFiles.push(newFileList.file)
+                                    setQueryForm({ ...queryForm, files: newFiles })
+                                }}
+                                style={{ marginRight: 0 }}
+                            >
+                                普通上传
+                            </Upload>
+                            <Upload
+                                listType="picture-card"
+                                accept='image/gif,image/jpeg,image/png,image/jpg'
+                                beforeUpload={(file: RcFile) => {
+                                    return false
+                                }}
+                                fileList={[]}
+                                onChange={(newFileList: any) => {
+                                    setFile(newFileList.file)
+                                    setVisible(true)
+                                }}
+                            >
+                                裁剪上传
+                            </Upload>
+                        </Space>
+                    </Space>
+
+                </Form.Item>
+            </Form>
+        </Modal>
+
+        {/* 裁剪 */}
+        {visible && <CropperImg visible={visible} onClose={() => setVisible(false)} file={file} onChange={(fileList: any[], file: any) => { 
+            let newFiles: any[] = queryForm.files
+            newFiles.push(file)
+            setQueryForm({ ...queryForm, files: newFiles }); 
+            setVisible(false) 
+        }} />}
+    </div>
+})
+export default React.memo(ImgsModal)

+ 9 - 5
src/components/FileBoxAD/index.tsx

@@ -8,6 +8,7 @@ import ImgModal from "./components/imgModal"
 import TreeBox from "./components/tree"
 import SortModal from "./components/fileModal/sortModal"
 import { EyeOutlined } from "@ant-design/icons"
+import ImgsModal from "./components/imgsModal"
 
 interface News {
     id: number,
@@ -53,7 +54,7 @@ function FlieBox(props: Props) {
 
     const { isAll = true, height, noFile = false, setPage, isBack = false } = props
     const { state, set, dels, list, get, onFile, allFile, delPupOn, delPupOff, changeClickFile, editFile, fileClick, treeClick, pathClick, getList, edit_media_folder, get_folder_tree } = useModel(isAll ? 'useLaunchAdq.useBdMedia' : 'useLaunchAdq.useBdMediaPup')
-    const { fileVisible, belongUser, selectFile, selectItem, delPupId, xy, rightClickPup, path, publicPath, parentId, imgVisrible, mediaType, sortVisible } = state
+    const { fileVisible, belongUser, selectFile, selectItem, delPupId, xy, rightClickPup, path, publicPath, parentId, imgVisrible, imgsVisrible, mediaType, sortVisible, num } = state
     const { copy } = useCopy()
     const fileImg = require('../../../public/file.png')
     const [moveId, setMoveId] = useState<any>('')//移动的素材ID
@@ -74,7 +75,7 @@ function FlieBox(props: Props) {
         if (props.isItem && isAll) {
             return <ul style={{ top: xy?.y, left: xy?.x }} className={style.menu} >
                 {
-                    isAll && <li onClick={allFile}>全选/反选</li>
+                    isAll || num === 100 && <li onClick={allFile}>全选/反选</li>
                 }
                 {mediaType === 'PAGE' ? <>
                     {/* <li onClick={() => { setPage && setPage(1, rightClickPup.id) }}>查看</li> */}
@@ -94,7 +95,7 @@ function FlieBox(props: Props) {
         }
         return <ul style={{ top: xy?.y, left: xy?.x }} className={style.menu}>
             {
-                isAll && <li onClick={allFile}>全选/反选</li>
+                isAll || num === 100 && <li onClick={allFile}>全选/反选</li>
             }
             {//防止K图文无限嵌套创建判断
                 (isAll !== false) && <li onClick={() => { set({ fileVisible: true }) }}>新建文件夹</li> //: <li>此处无法新建操作</li>
@@ -102,6 +103,9 @@ function FlieBox(props: Props) {
             {
                 mediaType === 'PAGE' ? isAll ? <li onClick={() => { setPage && setPage(0) }}>新建素材</li> : <li>此处无法新建操作</li> : <li onClick={() => { set({ imgVisrible: true }) }}>新建素材</li>
             }
+            {
+                mediaType === 'IMG' && <li onClick={() => { set({ imgsVisrible: true }) }}>批量新建素材</li>
+            }
             {
                 isAll && <li onClick={dels} style={{ color: 'red' }}>删除选中文件</li>
             }
@@ -183,8 +187,7 @@ function FlieBox(props: Props) {
             },
         }
     }, [folderId, moveId, mediaType])
-
-
+    
     return <div style={{ display: 'flex', flexFlow: 'row' }}>
         {get_folder_tree?.data?.length > 0 && <div style={{ flexShrink: 0 }}>
             {treeEl}
@@ -339,6 +342,7 @@ function FlieBox(props: Props) {
                 {sortVisible && <SortModal isAll={isAll} />}
                 {/* 新建非图文 */}
                 {imgVisrible && <ImgModal isAll={isAll} />}
+                {imgsVisrible && <ImgsModal isAll={isAll} />}
             </div>
             {
                 listData?.records?.length > 0 && <div className={style.pagination}>

+ 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, debounceInterval: 100 })
+    /**获取起来计划列表5min*/
+    const getMinuteList = useAjax((params: ListType) => getMinuteListApi(params), { formatResult: true, debounceInterval: 100 })
+    /** 获取今日计划总消耗图谱 */
+    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, debounceInterval: 500 })
+    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
+    }
+}

+ 115 - 11
src/models/useLaunchAdq/useBdMedia.ts

@@ -1,6 +1,6 @@
 import getMD5 from '@/components/MD5';
 import { useAjax } from '@/Hook/useAjax';
-import { bdSysMediaList, bdSysMediaAdd, delMedia, bdSysMediaEdit, getFileUrl, getMedia, configSortApi, getFolderTree, editMediaFolder } from '@/services/launchAdq/material';
+import { bdSysMediaList, bdSysMediaAdd, delMedia, bdSysMediaEdit, getFileUrl, getMedia, configSortApi, getFolderTree, editMediaFolder, bdSysMediaAddsApi } from '@/services/launchAdq/material';
 import { blobToBase64, dataURLtoFile, videoMessage } from '@/utils/compress';
 import { getImgSize } from '@/utils/utils';
 import { message } from 'antd';
@@ -11,6 +11,7 @@ import { request } from 'umi';
 type State = {
     fileVisible?: boolean,//文件夹弹窗
     imgVisrible?: boolean,//img,voice,video弹窗
+    imgsVisrible?: boolean,//img,voice,video弹窗
     newsVisrible?: boolean,//news弹窗
     knewsVisrible?: boolean,//客服图文弹窗
     fileName?: string,//文件夹名称
@@ -33,7 +34,7 @@ type State = {
     selectItem?: any,//单选素材时存放选中的素材
     knewsdefaultData?: any,//k图文编辑时的默认内容
     sortVisible?: boolean,//排序弹窗
-
+    num?: number
 
     size?: number,  // 需要上传素材的大小
     upLoadLoading?: boolean,  // 上传loading
@@ -78,6 +79,7 @@ const initData: State = {
     publicPath: [{ title: '公共本地', number: '0' }],
     path: [{ title: '个人本地', number: '0' }],
     imgVisrible: false,
+    imgsVisrible: false,
     newsVisrible: false,
     sortVisible: false,
     sort: 0,
@@ -88,6 +90,7 @@ function useBdMediaPup() {
     const { fileName, sort, belongUser, mediaType, parentId, selectFile, delPupId, rightClickPup, actionItem, path, publicPath, size, videoTitle, videoDescription } = state
     const list = useAjax((params) => bdSysMediaList(params))
     const add = useAjax((params) => bdSysMediaAdd(params), { msgNmae: '新增' })
+    const adds = useAjax((params) => bdSysMediaAddsApi(params), { msgNmae: '新增' })
     const del = useAjax((params) => delMedia(params), { msgNmae: '删除' })
     const edit = useAjax((params) => bdSysMediaEdit(params), { msgNmae: '编辑' })
     const configSort = useAjax((params) => configSortApi(params), { msgNmae: '排序' })
@@ -141,15 +144,15 @@ function useBdMediaPup() {
 
                 if (mediaType === 'IMG') {
                     if (file?.size > fileSize) { // 大于300kb进入压缩
-                        let bole = await compressAccurately(file, 250)
-                        if (bole?.size > 300000) {
-                            bole = await compressAccurately(file, 200)
+                        let bole = await compressAccurately(file, fileSize / 1024 - 50)
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - 100)
                         }
-                        if (bole?.size > 300000) {
-                            bole = await compressAccurately(file, 150)
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - 150)
                         }
-                        if (bole?.size > 300000) {
-                            bole = await compressAccurately(file, 100)
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - 200)
                         }
                         let newFile = await blobToBase64(bole)
                         message.warning({
@@ -182,7 +185,6 @@ function useBdMediaPup() {
                 /**修改文件名以用户设置的文件title命名*/
                 let newFile = new File([file], data?.title ? data?.title + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type })
                 let formData = new FormData();
-
                 /**向阿里云请求上传地址*/
                 fileUrl.run({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }).then(res1 => {
                     Object.keys(res1).forEach((key: string) => {
@@ -219,6 +221,107 @@ function useBdMediaPup() {
             }
         }
     }
+
+    /** 批量新增图片 视频 */
+    const addFiles = async (data: any) => {
+        if (data) {//存在代表素材
+            if (!data) {
+                return
+            }
+            let { files, ...value } = data
+            let params = files?.map(async (item: any, index: number) => {
+                let file = item
+                let fileSize = size || 0
+                if (mediaType === 'IMG') {
+                    fileSize = value?.fileSize ? value?.fileSize * 1024 : 307200
+                } else {
+                    fileSize = 104857600
+                }
+
+                if (mediaType === 'IMG') {
+                    let bad = 50
+                    let size = fileSize / 1024
+                    if (size > 10 && size <= 50) {
+                        bad = 5
+                    } else if (size > 50 && size <= 100) {
+                        bad = 10
+                    } else if (size > 100 && size <= 300) {
+                        bad = 20
+                    } else {
+                        bad = 50
+                    }
+                    if (file?.size > fileSize) { // 大于300kb进入压缩
+                        let bole = await compressAccurately(file, fileSize / 1024 - bad)
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - bad * 2)
+                        }
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - bad * 3)
+                        }
+                        if (bole?.size > fileSize) {
+                            bole = await compressAccurately(file, fileSize / 1024 - bad * 4)
+                        }
+                        let newFile = await blobToBase64(bole)
+                        message.warning({
+                            content: `选择的图片大于${fileSize / 1024}KB,图片已压缩`,
+                            duration: 3
+                        })
+                        file = await dataURLtoFile(newFile, file?.name)
+                    }
+                } else if (mediaType === 'VIDEO') {
+                    if (file?.size > fileSize) { // 大于100mb进入压缩
+                        message.error({
+                            content: `选择的视频大于${fileSize / 1024 / 1024}MB,请重新选择提交`,
+                            duration: 3
+                        })
+                        return
+                    }
+                }
+                let width = 0
+                let height = 0
+                if (mediaType === 'IMG') {
+                    let imgData = await getImgSize(file)
+                    width = imgData.width
+                    height = imgData.height
+                } else if (mediaType === "VIDEO") {
+                    let videoInfo: any = await videoMessage([file])
+                    width = videoInfo[0].width
+                    height = videoInfo[0].height
+                }
+                /**修改文件名以用户设置的文件title命名*/
+                let newFile = new File([file], data?.title ? data?.title + index + 1 + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type })
+                let formData = new FormData();
+                set({ upLoadLoading: true })
+                let res = await getFileUrl({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }).catch(() => set({ upLoadLoading: false }))
+                let res1 = res.data
+                Object.keys(res1).forEach((key: string) => {
+                    if (key !== 'url') {
+                        formData.append(key, res1[key])
+                    }
+                })
+                formData.append('file', newFile)
+                /**向阿里云返回的上传地址上传文件*/
+                let data1: { code: number, data: { url: string } } = await request(res1?.ossUrl, { method: 'post', body: formData }).catch(() => set({ upLoadLoading: false }))
+                message.success('上传成功')
+                let fileMd5 = await getMD5(newFile)
+                let obj = { title: data.title ? data.title + '_' + (index + 1).toString() : data.title, folder: false, parentId, width, height, fileMd5, url: data1?.data?.url, sort: data?.sort, fileSize: newFile?.size, fileMime: newFile.type }
+                if (mediaType === 'VIDEO') {
+                    obj['videoTitle'] = data?.videoTitle || data?.title
+                    obj['videoDescription'] = data?.videoDescription
+                }
+                return { ...obj }
+            })
+
+            Promise.all(params).then(res => {
+                adds.run({ belongUser: belongUser === '0' ? false : true, data: res }).then(response => {
+                    list.refresh()
+                    offEditFile()//关闭弹窗并清空相关数据
+                    set({ upLoadLoading: false })
+                }).catch(() => set({ upLoadLoading: false }))
+            })
+        }
+    }
+
     /**编辑非图文素材名称*/
     const nameOk = useCallback((selectWx?: any) => {
         if (fileName && actionItem) {
@@ -378,7 +481,7 @@ function useBdMediaPup() {
     }, [rightClickPup])
     /**取消编辑后清空选中存放的数据并关闭弹窗*/
     const offEditFile = useCallback(() => {
-        set({ fileVisible: false, imgVisrible: false, sortVisible: false, actionItem: '', fileName: '', selectFile: selectFile?.filter(id => id !== actionItem?.id), sort: 0 })
+        set({ fileVisible: false, imgVisrible: false, imgsVisrible: false, sortVisible: false, actionItem: '', fileName: '', selectFile: selectFile?.filter(id => id !== actionItem?.id), sort: 0 })
     }, [selectFile, actionItem])
     /**全选反选文件*/
     const allFile = useCallback(() => {
@@ -407,6 +510,7 @@ function useBdMediaPup() {
         fileClick,
         treeClick,
         pathClick,
+        addFiles,
         configSort,
         fileUrl,
         list,

+ 116 - 14
src/models/useLaunchAdq/useBdMediaPup.ts

@@ -10,6 +10,7 @@ import {
   configSortApi,
   getFolderTree,
   editMediaFolder,
+  bdSysMediaAddsApi,
 } from '@/services/launchAdq/material';
 import { blobToBase64, dataURLtoFile, videoMessage } from '@/utils/compress';
 import { getImgSize } from '@/utils/utils';
@@ -21,6 +22,7 @@ import { request } from 'umi';
 type State = {
   fileVisible?: boolean; //文件夹弹窗
   imgVisrible?: boolean; //img,voice,video弹窗
+  imgsVisrible?: boolean,//
   newsVisrible?: boolean; //news弹窗
   knewsVisrible?: boolean; //客服图文弹窗
   fileName?: string; //文件夹名称
@@ -96,6 +98,7 @@ const initData: State = {
   publicPath: [{ title: '公共本地', number: '0' }],
   path: [{ title: '个人本地', number: '0' }],
   imgVisrible: false,
+  imgsVisrible: false,
   newsVisrible: false,
   sortVisible: false,
   sort: 0,
@@ -137,6 +140,7 @@ function useBdMediaPup() {
   const del = useAjax((params) => delMedia(params), { msgNmae: '删除' });
   const edit = useAjax((params) => bdSysMediaEdit(params), { msgNmae: '编辑' });
   const configSort = useAjax((params) => configSortApi(params), { msgNmae: '排序' });
+  const adds = useAjax((params) => bdSysMediaAddsApi(params), { msgNmae: '新增' })
   const get = useAjax((params) => getMedia(params)); //获取图文详情
   const get_folder_tree = useAjax((params: any) => getFolderTree(params));
   const edit_media_folder = useAjax((params: any) => editMediaFolder(params));
@@ -302,6 +306,107 @@ function useBdMediaPup() {
       }
     }
   };
+
+  /** 批量新增图片 视频 */
+  const addFiles = async (data: any) => {
+    if (data) {//存在代表素材
+      if (!data) {
+        return
+      }
+      let { files, ...value } = data
+      let params = files?.map(async (item: any, index: number) => {
+        let file = item
+        let fileSize = size || 0
+        if (mediaType === 'IMG') {
+          fileSize = value?.fileSize ? value?.fileSize * 1024 : 307200
+        } else {
+          fileSize = 104857600
+        }
+
+        if (mediaType === 'IMG') {
+          let bad = 50
+          let size = fileSize / 1024
+          if (size > 10 && size <= 50) {
+            bad = 5
+          } else if (size > 50 && size <= 100) {
+            bad = 10
+          } else if (size > 100 && size <= 300) {
+            bad = 20
+          } else {
+            bad = 50
+          }
+          if (file?.size > fileSize) { // 大于300kb进入压缩
+            let bole = await compressAccurately(file, fileSize / 1024 - bad)
+            if (bole?.size > fileSize) {
+              bole = await compressAccurately(file, fileSize / 1024 - bad * 2)
+            }
+            if (bole?.size > fileSize) {
+              bole = await compressAccurately(file, fileSize / 1024 - bad * 3)
+            }
+            if (bole?.size > fileSize) {
+              bole = await compressAccurately(file, fileSize / 1024 - bad * 4)
+            }
+            let newFile = await blobToBase64(bole)
+            message.warning({
+              content: `选择的图片大于${fileSize / 1024}KB,图片已压缩`,
+              duration: 3
+            })
+            file = await dataURLtoFile(newFile, file?.name)
+          }
+        } else if (mediaType === 'VIDEO') {
+          if (file?.size > fileSize) { // 大于100mb进入压缩
+            message.error({
+              content: `选择的视频大于${fileSize / 1024 / 1024}MB,请重新选择提交`,
+              duration: 3
+            })
+            return
+          }
+        }
+        let width = 0
+        let height = 0
+        if (mediaType === 'IMG') {
+          let imgData = await getImgSize(file)
+          width = imgData.width
+          height = imgData.height
+        } else if (mediaType === "VIDEO") {
+          let videoInfo: any = await videoMessage([file])
+          width = videoInfo[0].width
+          height = videoInfo[0].height
+        }
+        /**修改文件名以用户设置的文件title命名*/
+        let newFile = new File([file], data?.title ? data?.title + index + 1 + '.' + file?.name?.split('.')[1] : file?.name, { type: file?.type })
+        let formData = new FormData();
+        set({ upLoadLoading: true })
+        let res = await getFileUrl({ type: newFile.type, fileType: mediaType === 'VIDEO' ? 'video' : 'image' }).catch(() => set({ upLoadLoading: false }))
+        let res1 = res.data
+        Object.keys(res1).forEach((key: string) => {
+          if (key !== 'url') {
+            formData.append(key, res1[key])
+          }
+        })
+        formData.append('file', newFile)
+        /**向阿里云返回的上传地址上传文件*/
+        let data1: { code: number, data: { url: string } } = await request(res1?.ossUrl, { method: 'post', body: formData }).catch(() => set({ upLoadLoading: false }))
+        message.success('上传成功')
+        let fileMd5 = await getMD5(newFile)
+        let obj = { title: data.title + (index + 1).toString(), folder: false, parentId, width, height, fileMd5, url: data1?.data?.url, sort: data?.sort, fileSize: newFile?.size, fileMime: newFile.type }
+        if (mediaType === 'VIDEO') {
+          obj['videoTitle'] = data?.videoTitle || data?.title
+          obj['videoDescription'] = data?.videoDescription
+        }
+        return { ...obj }
+      })
+
+      Promise.all(params).then(res => {
+        adds.run({ belongUser: belongUser === '0' ? false : true, data: res }).then(response => {
+          list.refresh()
+          offEditFile()//关闭弹窗并清空相关数据
+          set({ upLoadLoading: false })
+        }).catch(() => set({ upLoadLoading: false }))
+      })
+    }
+  }
+
   /**编辑非图文素材名称*/
   const nameOk = useCallback(
     (selectWx: any) => {
@@ -377,7 +482,7 @@ function useBdMediaPup() {
         setIsOk(true);
       });
     },
-    [list, mediaType, belongUser, parentId, cloudSize, maxSize,adcreativeTemplateId,promotedObjectType,promotedObjectId,pageType,canvasType,siteSet,sourceType,marketingScene],
+    [list, mediaType, belongUser, parentId, cloudSize, maxSize, adcreativeTemplateId, promotedObjectType, promotedObjectId, pageType, canvasType, siteSet, sourceType, marketingScene],
   );
   /**选中文件 single 开启可单选为了在右键删除选择时只选一个*/
   const onFile = useCallback(
@@ -474,20 +579,15 @@ function useBdMediaPup() {
   );
   /**判断是取消还是选中文件的操作在点击打钩时使用 */
   const changeClickFile = useCallback(
-    (e: any, item: { id: any; folder?: boolean }, isAll?: boolean, noFile?: boolean) => {
-      let { id } = item;
+    (e: any, item: { id: any; folder?: boolean, url: string }, isAll?: boolean, noFile?: boolean) => {
+      let { id, url } = item;
       e?.stopPropagation(); //阻止冒泡传递到文件夹被点击事件
-      console.log(111111, num);
-
-      // if (mediaType === 'PAGE') {
-
-      // } else {
-      let state = selectFile?.some((i) => i === id);
+      let state = selectItem?.some((i) => i.url === url);
       if (state) {
         //存在就是删除
         set({
           selectFile: selectFile?.filter((i) => i !== id),
-          selectItem: selectItem?.filter((i: { id: number }) => i.id !== id),
+          selectItem: selectItem?.filter((i: { url: string }) => i.url !== url),
         });
       } else {
         //否则新增
@@ -497,7 +597,6 @@ function useBdMediaPup() {
             set({ selectFile: selectFile?.filter((i) => i !== id), selectItem: [] });
           } else {
             //否则新增
-            console.log(11111);
             set({ selectFile: [id], selectItem: [item] });
           }
         } else {
@@ -549,6 +648,7 @@ function useBdMediaPup() {
     set({
       fileVisible: false,
       imgVisrible: false,
+      imgsVisrible: false,
       sortVisible: false,
       actionItem: '',
       fileName: '',
@@ -558,12 +658,13 @@ function useBdMediaPup() {
   }, [selectFile, actionItem]);
   /**全选反选文件*/
   const allFile = useCallback(() => {
-    let allArr: any[] = [];
+    let allArr: any[] = [], allFile: any[] = [];
     list?.data?.records?.forEach((item: { id: any }) => {
       allArr.push(item.id);
+      allFile.push(item)
     });
-    set({ selectFile: allArr.filter((i) => selectFile?.every((id) => id !== i)) });
-  }, [selectFile, list, mediaType]);
+    set({ selectFile: allArr.filter((i) => selectFile?.every((id) => id !== i)), selectItem: allFile.filter((i) => selectItem?.every((item: any) => item.id !== i.id)) });
+  }, [selectFile, list, mediaType, selectItem]);
   return {
     state,
     init,
@@ -583,6 +684,7 @@ function useBdMediaPup() {
     fileClick,
     treeClick,
     pathClick,
+    addFiles,
     configSort,
     list,
     add,

+ 6 - 2
src/models/useOperating/useWxGroupList.ts

@@ -1,5 +1,5 @@
 import { useCallback, useEffect, useReducer } from 'react';
-import { allOfMember, listGroup, getAllZhMemBerApi } from '@/services/operating/account';
+import { allOfMember, listGroup, getAllZhMemBerApi, getPicherListApi } from '@/services/operating/account';
 import { useAjax } from '@/Hook/useAjax';
 import { getWxlist } from '@/services/operating/material';
 import { getMpsTimeOut } from '@/services/user';
@@ -80,6 +80,7 @@ export default function useWxGroupList() {
   const getAllZhMemBer = useAjax(() => getAllZhMemBerApi()); //获取账户下属公众号
   const catWx = useAjax((userId) => getMpsTimeOut(userId)); //查询公众号是否过期
   const getversions = useAjax(() => getVersions()); //获取版本号
+  const getPicherList = useAjax(() => getPicherListApi()); //获取投手
   const getWxlists = useAjax((id) => getWxlist(id));
   const [state, dispatch] = useReducer(weChatReducer, {
     tabsKey: '自己',
@@ -165,7 +166,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) => {
@@ -272,5 +275,6 @@ export default function useWxGroupList() {
     initSelectWx,
     getWxlists,
     getversions,
+    getPicherList
   };
 }

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 30 - 0
src/pages/adMonitor/adMonitorList/components/box.tsx


+ 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/adMonitor/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?.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

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

@@ -0,0 +1,376 @@
+/**起量表原始列表*/
+const qiliangpaihang = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '时间', dataIndex: 'currTime', label: '设置信息', default: 1, width: 135 },
+            { 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: '设置信息', width: 80 },
+            // { title: '投放位置', dataIndex: 'pitchSeat', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息' },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', default: 12, width: 135 },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息', width: 85 },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectType', label: '设置信息' },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 60 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'dailyBudget', label: '费用', width: 80 },
+            { title: '出价方式', dataIndex: 'bidMode', label: '费用', width: 65 },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用', default: 24, width: 100 },
+            { title: '今日消耗', dataIndex: 'costDay', label: '费用', default: 15, width: 90 },
+            { title: '当前小时消耗', dataIndex: 'costHour', label: '费用', default: 16, width: 80 },
+            { title: '前第1小时消耗', dataIndex: 'costLastHour', label: '费用', default: 17, width: 80 },
+            { title: '前第2小时消耗', dataIndex: 'costLastTwoHour', label: '费用', default: 18, width: 80 },
+            { title: '前第3小时消耗', dataIndex: 'costLastThreeHour', label: '费用', default: 19, width: 80 },
+            { title: '当前小时消耗差额', dataIndex: 'costDiffBeforeHour', label: '费用', default: 20, width: 80 },
+            { title: '前第1小时消耗差额', dataIndex: 'costDiffBeforeTwoHour', label: '费用', default: 21, width: 90 },
+            { title: '前第2小时消耗差额', dataIndex: 'costDiffBeforeThreeHour', label: '费用', default: 22, width: 90 },
+            { title: '前三小时消耗趋势', dataIndex: 'costTrendLastThreeHour', label: '费用', default: 23, width: 70 },
+            { title: '当前5min消耗流速', dataIndex: 'costSpeed', label: '费用', default: 14, width: 70 },
+        ]
+    },
+    {
+        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 qiliangpaihanghour = [
+    {
+        label: '设置信息',
+        data: [
+            { title: '时间', dataIndex: 'currTime', label: '设置信息', default: 1, width: 135 },
+            { 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: '设置信息', width: 80 },
+            // { title: '投放位置', dataIndex: 'pitchSeat', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息' },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', default: 12, width: 135 },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息', width: 85 },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectType', label: '设置信息' },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 60 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'dailyBudget', label: '费用', width: 80 },
+            { title: '出价方式', dataIndex: 'bidMode', label: '费用', width: 65 },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用', default: 24, width: 100 },
+            { title: '今日消耗', dataIndex: 'costDay', label: '费用', default: 15, width: 90 },
+            { title: '当前小时消耗', dataIndex: 'costHour', label: '费用', default: 16, width: 80 },
+            { title: '前第1小时消耗', dataIndex: 'costLastHour', label: '费用', default: 17, width: 80 },
+            { title: '前第2小时消耗', dataIndex: 'costLastTwoHour', label: '费用', default: 18, width: 80 },
+            { title: '前第3小时消耗', dataIndex: 'costLastThreeHour', label: '费用', default: 19, width: 80 },
+            { title: '当前小时消耗差额', dataIndex: 'costDiffBeforeHour', label: '费用', default: 20, width: 80 },
+            { title: '前第1小时消耗差额', dataIndex: 'costDiffBeforeTwoHour', label: '费用', default: 21, width: 90 },
+            { title: '前第2小时消耗差额', dataIndex: 'costDiffBeforeThreeHour', label: '费用', default: 22, width: 90 },
+            { title: '前三小时消耗趋势', dataIndex: 'costTrendLastThreeHour', label: '费用', default: 23, width: 70 },
+            { title: '当前5min消耗流速', dataIndex: 'costSpeed', label: '费用', default: 14, width: 70 },
+        ]
+    },
+    {
+        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, default: 5 },
+            // { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiDay', label: '商品转化', 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: 'currTime', label: '设置信息', default: 1, width: 135 },
+            { 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: '设置信息', width: 80 },
+            // { title: '投放位置', dataIndex: 'pitchSeat', label: '设置信息' },
+            // { title: '自动扩量', dataIndex: 'autoExpand', label: '设置信息' },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            // { title: '曝光评估', dataIndex: 'impressionAppraise', label: '设置信息' },
+            { title: '投放时间', dataIndex: 'adBeginTime', label: '设置信息', default: 12, width: 135 },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息', width: 85 },
+            // { title: '创意状态', dataIndex: 'adCreativeStatus', label: '设置信息' },
+            { title: '推广目标', dataIndex: 'promotedObjectType', label: '设置信息' },
+            // { title: '购买类型', dataIndex: 'adBuyType', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 60 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'dailyBudget', label: '费用', width: 80 },
+            { title: '出价方式', dataIndex: 'bidMode', label: '费用', width: 65 },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用', width: 80 },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用' },
+            { title: '广告总消耗', dataIndex: 'costTotal', label: '费用', width: 100 },
+            { title: '今日消耗', dataIndex: 'costDay', label: '费用', width: 90 },
+            { title: '当前小时消耗', dataIndex: 'costHour', label: '费用', width: 80 },
+            // { title: '前第1小时消耗', dataIndex: 'costLastHour', label: '费用', width: 80, disabled: true },
+            // { title: '前第2小时消耗', dataIndex: 'costLastTwoHour', label: '费用', width: 80, disabled: true },
+            // { title: '前第3小时消耗', dataIndex: 'costLastThreeHour', label: '费用', width: 80, disabled: true },
+            // { title: '当前小时消耗差额', dataIndex: 'costDiffBeforeHour', label: '费用', width: 80, disabled: true },
+            // { title: '前第1小时消耗差额', dataIndex: 'costDiffBeforeTwoHour', label: '费用', width: 90, disabled: true },
+            // { title: '前第2小时消耗差额', dataIndex: 'costDiffBeforeThreeHour', label: '费用', width: 90, disabled: true },
+            // { title: '前三小时消耗趋势', dataIndex: 'costTrendLastThreeHour', label: '费用', width: 80, disabled: true },
+            { title: '当前5min消耗流速', dataIndex: 'costSpeed', label: '费用', default: 14, width: 70 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'viewDay', label: '曝光', default: 3, width: 70 },
+            // { title: '千次曝光成本', dataIndex: 'thousandDisplayPriceDay', label: '曝光', 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: '转化指标组', default: 8, width: 120 },
+            { title: '转化目标量', dataIndex: 'conversionsCountDay', label: '转化指标组', default: 7, width: 75 },
+            // { title: '转化目标成本', dataIndex: 'conversionsCostDay', label: '转化指标组', width: 75 },
+            // { title: '目标转化率', dataIndex: 'conversionsRateDay', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'orderCountDay', label: '商品转化', width: 70, default: 4 },
+            // { 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, default: 5 },
+            // { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoiDay', label: '商品转化', 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: '设置信息', width: 80 },
+            { title: '广告账户', dataIndex: 'accountId', label: '设置信息', default: 5, width: 70 },
+            { title: '投手', dataIndex: 'putUserName', label: '设置信息', default: 6, width: 65 },
+            { title: '创建日期', dataIndex: 'adCreateTime', label: '设置信息' },
+            { title: '广告状态', dataIndex: 'adStatus', label: '设置信息', default: 7, width: 85 },
+            // { 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: 'promotedObjectType', label: '设置信息', default: 9 },
+            // { title: '深度优化目标', dataIndex: 'depthOptimizeTarget', label: '设置信息' },
+            { title: '操作', dataIndex: 'event', label: '设置信息', default: 13, width: 75 },
+        ]
+    },
+    {
+        label: '费用',
+        data: [
+            { title: '广告预算', dataIndex: 'adBudget', label: '费用', default: 10, width: 90 },
+            { title: '出价方式', dataIndex: 'bidMode', label: '费用', width: 65 },
+            { title: '当前出价', dataIndex: 'bidAmount', label: '费用' },
+            // { title: '计划预算', dataIndex: 'planBudget', label: '费用', default: 19 },
+            { title: '广告总消耗', dataIndex: 'cost', label: '费用' },
+            // { title: '今日消耗', dataIndex: 'todayCostTotal', label: '费用' },
+            { title: '单位时间消耗速度(元/小时)', dataIndex: 'costSpeed', label: '费用', default: 11, width: 80 },
+        ]
+    },
+    {
+        label: '曝光',
+        data: [
+            { title: '曝光量(曝光次数)', dataIndex: 'viewCount', label: '曝光' },
+            { title: '千次曝光成本', dataIndex: 'thousandDisplayPrice', label: '曝光' },
+        ]
+    },
+    {
+        label: '点击',
+        data: [
+            { title: '点击量(点击次数)', dataIndex: 'clickCount', label: '点击', width: 75 },
+            { title: '点击均价', dataIndex: 'avgClickAmount', label: '点击', width: 75 },
+            { title: '点击率', dataIndex: 'clickRate', label: '点击', default: 12, width: 70 },
+            { title: '不感兴趣点击次数', dataIndex: 'noInterestCount', label: '点击', width: 85 },
+        ]
+    },
+    {
+        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 },
+            { title: '深度转化量', dataIndex: 'deepConversionsCount', label: '转化指标组', width: 75 },
+        ]
+    },
+    {
+        label: '商品转化',
+        data: [
+            { title: '下单量', dataIndex: 'orderCount', label: '商品转化', width: 70 },
+            { title: '首日新增下单量', dataIndex: 'firstDayOrderCount', label: '商品转化', width: 80 },
+            { title: '下单成本', dataIndex: 'orderCost', label: '商品转化', width: 75 },
+            { title: '下单率', dataIndex: 'orderRate', label: '商品转化', width: 70 },
+            { title: '下单金额', dataIndex: 'orderAmount', label: '商品转化', width: 70 },
+            { title: '首日新增下单金额', dataIndex: 'firstDayOrderAmount', label: '商品转化', width: 75 },
+            { title: '下单客单价', dataIndex: 'atvAmount', label: '商品转化', width: 70 },
+            { title: '下单ROI', dataIndex: 'orderROI', label: '商品转化', width: 75 },
+            { title: '首日新增下单ROI', dataIndex: 'firstDayOrderRoi', 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, qiliangpaihanghour }

+ 19 - 0
src/pages/adMonitor/adMonitorList/data.tsx

@@ -0,0 +1,19 @@
+import { Badge } from "antd";
+import React from "react";
+
+/**广告状态 */
+export const GGStateData = {
+    'STATUS_PENDING': '审核中',
+    'STATUS_DENIED': <Badge status="error" text={<span style={{ fontSize: 12 }}>审核不通过</span>}/>,
+    'STATUS_SUSPEND': <Badge status="warning" text={<span style={{ fontSize: 12 }}>暂停中</span>}/>,
+    'STATUS_READY': <Badge status="default" text={<span style={{ fontSize: 12 }}>未到投放时间</span>}/>,
+    'STATUS_ACTIVE': <Badge status="processing" text={<span style={{ fontSize: 12 }}>投放中</span>}/>,
+    'STATUS_STOP': '投放结束',
+    'STATUS_ACTIVE_CAMPAIGN_SUSPEND': '广告被暂停',
+    'STATUS_PART_READY': '部分待投放',
+    'STATUS_PART_ACTIVE': '部分投放中',
+    'STATUS_DELETED': '已删除',
+    'STATUS_UNKNOWN': '未知状态',
+    'STATUS_FROZEN': '冻结',
+    'STATUS_PREPARE': '准备中'
+}

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

@@ -0,0 +1,129 @@
+/**购买类型*/
+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 {
+    'PROMOTED_OBJECT_TYPE_APP_ANDROID' = 'Android 应用',
+    'PROMOTED_OBJECT_TYPE_APP_IOS' = 'IOS 应用',
+    'PROMOTED_OBJECT_TYPE_ECOMMERCE' = '电商推广',
+    'PROMOTED_OBJECT_TYPE_LINK_WECHAT' = '品牌活动推广',
+    'PROMOTED_OBJECT_TYPE_APP_ANDROID_MYAPP' = '应用宝推广',
+    'PROMOTED_OBJECT_TYPE_LOCAL_ADS_WECHAT' = '本地广告',
+    'PROMOTED_OBJECT_TYPE_QQ_BROWSER_MINI_PROGRAM' = 'QQ 浏览器小程序',
+    'PROMOTED_OBJECT_TYPE_QQ_MESSAGE' = 'QQ 消息',
+    'PROMOTED_OBJECT_TYPE_QZONE_VIDEO_PAGE' = '视频说说',
+    'PROMOTED_OBJECT_TYPE_LEAD_AD' = '销售线索收集',
+    'PROMOTED_OBJECT_TYPE_MINI_GAME_WECHAT' = '微信小游戏',
+    'PROMOTED_OBJECT_TYPE_MINI_GAME_QQ' = 'QQ 小游戏',
+    'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT' = '微信公众号',
+    'PROMOTED_OBJECT_TYPE_APP_QUICK_APP' = '快应用',
+}
+/**优化目标*/
+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" = "阅读文章"
+}
+
+enum CHUJIAFANGSHI {
+    "BID_MODE_CPC" = "CPC",
+    "BID_MODE_CPA" = "CPA",
+    "BID_MODE_CPM" = "CPM",
+    "BID_MODE_OCPC" = "oCPC",
+    "BID_MODE_OCPM" = "oCPM"
+}
+export { GOUMAILEIXING, GUANGGAOZHUANGTAI, CHUANGYIZHUANGTAI, TUIGUANGMUBIAO, YOUHUAMUBIAO, CHUJIAFANGSHI }

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

@@ -0,0 +1,114 @@
+.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: 300px;
+            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;
+}
+
+.padding2 {
+    padding: 2px 5px !important;
+}

+ 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" size="small" 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

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

@@ -0,0 +1,474 @@
+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, EyeInvisibleOutlined, EyeOutlined, 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, qiliangpaihanghour, 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 { getPicherList } = useModel('useOperating.useWxGroupList')
+    // 变量开始
+    const [queryForm, setQueryForm] = useState<newListType>({ totalTimeUnit: 'day', planTimeUnit: 'hour', sysUserId: JSON.parse(sessionStorage.getItem('SYSUSERID') || '[]'), pageNum: 1, pageSize: 100 }) // 搜索变量//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 [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 [hour, setHour] = useState<any>()
+    const [clickAccountId, setClickAccountId] = useState<number[]>([])
+    const [showEacharts, setShowEacharts] = useState<boolean>(true)
+    const { totalTimeUnit, planTimeUnit, adgroup, accountId, sysUserId, pageNum, pageSize, campaign, sortField, sort } = queryForm
+    // 变量结束
+
+    // 获取投手
+    useEffect(() => {
+        getPicherList.run()
+    }, [])
+    // 获取广告账号
+    useEffect(() => {
+        getAdqAccountList.run()
+    }, [])
+    // // 获取排行数据,柱图
+    useEffect(() => {
+        getPlanCostList()
+    }, [totalTimeUnit, accountId, sysUserId])
+    // 获取今日计划总消耗图谱,折线
+    useEffect(() => {
+        getTootalCostList()
+    }, [planTimeUnit, adgroup, accountId, sysUserId])
+    // 获取起量计划列表 底部table
+    useEffect(() => {
+        if (mode === 'total') {
+            getList()
+        }
+    }, [accountId, sysUserId, pageNum, pageSize, campaign, adgroup, sortField, sort, mode, hour])
+
+    // 获取起量明细表 点击
+    useEffect(() => {
+        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])
+
+    /** 获取柱状图 */
+    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, accountId: number }) => {
+            return { name: item.adId.toString(), value: item.cost, adName: item.adName, accountId: item.accountId }
+        })
+        data = data?.sort((a: any, b: any) => {
+            var value1 = a['value'];
+            var value2 = b['value'];
+            return value2 - value1;
+        })
+        setBarDis(() => data)
+    }, [queryForm, barDis])
+
+    // 气量Table总表
+    const getList = useCallback(() => {
+        let { totalTimeUnit, planTimeUnit, timeUnit, sysUserId, accountId, ...newQueryForm } = queryForm
+        getPlanList.run({ ...newQueryForm, sysUserId, accountId, hour })
+    }, [queryForm, hour])
+
+
+    // 起量明细表 点击
+    const getDetailList = useCallback((adgroup: any, clickAccountId: any[]) => {
+        setClickAccountId(clickAccountId)
+        let { totalTimeUnit, planTimeUnit, timeUnit, pageNum, pageSize, adgroup: aa, sysUserId, accountId, ...newQueryForm } = queryForm
+        if (adgroup) {
+            setMode('detail')//切到明细
+            getPlanDetailList.run({ ...newQueryForm, adgroupId: adgroup, sysUserId, accountId: accountId ? accountId : clickAccountId }).then((res: any) => {
+                setPlanDetailList(res?.data ? [...res?.data] : [])
+                if (adgroup != aa) {
+                    setQueryForm({ ...queryForm, adgroup })
+                }
+            })
+        }
+    }, [queryForm, getPlanList, planDetailList])
+
+    // 详情
+    const details = (data: any) => {
+        setAId(data)
+        setVisible(true)
+    }
+
+    // 起量5min表
+    const getMinuList = useCallback((adgroup: any, clickAccountId: any[]) => {
+        setClickAccountId(clickAccountId)
+        let { totalTimeUnit, planTimeUnit, timeUnit, pageNum, pageSize, adgroup: aa, sysUserId, accountId, ...newQueryForm } = queryForm
+        if (adgroup) {
+            setMode('minute')
+            getMinuteList.run({ ...newQueryForm, adgroupId: adgroup, sysUserId, accountId: accountId ? accountId : clickAccountId }).then((res: any) => {
+                setMinuteList(res?.data ? [...res?.data] : [])
+                if (adgroup != aa) {
+                    setQueryForm({ ...queryForm, adgroup })
+                }
+            })
+        }
+    }, [queryForm, getPlanList, 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)
+        }
+    }, [])
+
+    //图形排列样式改变重新获取数据刷新图形
+    const set = useCallback((b) => {
+        setPx(b)
+        getTotalCost.refresh()
+        getPlanCost.refresh()
+    }, [getPlanCost, getTotalCost])
+
+    // 下载
+    const downLoadExcel = useCallback(() => {
+        let ajax: any = null
+        let params: any = {}
+        let { totalTimeUnit, planTimeUnit, timeUnit, sysUserId, accountId, adgroup, pageNum, pageSize, ...newQueryForm } = queryForm
+        switch (mode) {
+            case 'total':
+                params = { ...newQueryForm, sysUserId, accountId, adgroupId: adgroup }
+                ajax = downLoadUpAdApi
+                break;
+            case 'detail':
+                params = { ...newQueryForm, adgroupId: adgroup, accountId }
+                ajax = downLoadDetailApi
+                break;
+            case 'minute':
+                params = { ...newQueryForm, adgroupId: adgroup, accountId }
+                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, 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)
+        }
+    }
+
+    const disabledTime = () => {
+        let h = moment().format('H')
+        let H: number[] = []
+        for (let index = Number(h) + 1; index < 23; index++) {
+            H.push(index)
+        }
+        return {
+            disabledHours: () => H
+        }
+    }
+
+    return <Space direction='vertical' style={{ width: '100%' }} className="monitor">
+        <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
+            <div style={{ display: 'flex', justifyContent: 'space-between' }}>
+                <Space>
+                    <Select
+                        showSearch
+                        value={queryForm.sysUserId}
+                        style={{ minWidth: 180, maxWidth: 250 }}
+                        mode='multiple'
+                        maxTagCount={1}
+                        allowClear 
+                        placeholder="请选择投手"
+                        disabled={queryForm?.adgroup || queryForm?.accountId?.length > 0}
+                        onChange={(value: number[]) => {
+                            setQueryForm({ ...queryForm, sysUserId: value, pageNum: 1 })
+                            sessionStorage.setItem('SYSUSERID', value ? JSON.stringify(value) : '')
+                            if (mode === 'detail') {
+                                getDetailList(queryForm.adgroup, queryForm?.accountId)
+                            } else if (mode === 'minute') {
+                                getMinuList(queryForm.adgroup, queryForm?.accountId)
+                            }
+                        }}
+                        filterOption={(input, option) =>
+                            (option?.children as any).toLowerCase().indexOf(input.toLowerCase()) >= 0
+                        }
+                    >
+                        {getPicherList?.data?.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, maxWidth: 250 }}
+                        allowClear
+                        placeholder="请选择广告账号"
+                        onChange={(value: number[]) => {
+                            setQueryForm({ ...queryForm, accountId: value, pageNum: 1 })
+                        }}
+                    >
+                        {getAdqAccountList?.data?.data?.map((item: { id: number, accountId: number }) => <Select.Option
+                            value={item.accountId}
+                            key={item.id}
+                        >
+                            {item.accountId}
+                        </Select.Option>)}
+                    </Select>
+                    <Input
+                        value={queryForm.campaign}
+                        placeholder="计划ID"
+                        onChange={(e) => {
+                            let value = e.target.value
+                            if (!isNaN(Number(value))) {
+                                setQueryForm({ ...queryForm, campaign: value })
+                            }
+                            if (mode === 'detail') {
+                                getDetailList(queryForm?.adgroup, queryForm?.accountId)
+                            } else if (mode === 'minute') {
+                                getMinuList(queryForm?.adgroup, queryForm?.accountId)
+                            }
+                        }}
+                        allowClear
+                    />
+                    <Input
+                        value={queryForm.adgroup}
+                        placeholder="广告ID"
+                        onChange={(e) => {
+                            let value = e.target.value
+                            if (!isNaN(Number(value))) {
+                                setQueryForm({ ...queryForm, adgroup: e.target.value })
+                            }
+                            if (!value) {
+                                setClickAccountId([])
+                            }
+                            if (mode === 'detail') {
+                                getDetailList(e.target.value, queryForm?.accountId)
+                            } else if (mode === 'minute') {
+                                getMinuList(e.target.value, queryForm?.accountId)
+                            }
+                        }}
+                        allowClear
+                    />
+                </Space>
+                <Space>
+                    <Tag onClick={() => setShowEacharts(!showEacharts)}>{showEacharts ? <><EyeInvisibleOutlined /> 隐藏</> : <><EyeOutlined /> 显示</>}</Tag>
+                    <Tag color="#2db7f5" onClick={refresh}><RedoOutlined />   刷新</Tag>
+                </Space>
+            </div>
+        </Card>
+        {showEacharts && <Card hoverable bodyStyle={{ padding: '12px 16px' }}>
+            <span style={{ position: 'absolute', top: 10, zIndex: 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, accountId) => { getDetailList(value, accountId) }} 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
+                bodyStyle={{ padding: '12px 16px' }}
+                gutter={[0, 12]}
+                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 : getMinuteList?.data?.data}
+                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
+                            switch (value) {
+                                case 'total':
+                                    setMode(value)
+                                    break;
+                                case 'detail':
+                                    getDetailList(queryForm.adgroup, clickAccountId)
+                                    break
+                                case 'minute':
+                                    getMinuList(queryForm.adgroup, clickAccountId)
+                                    break
+                            }
+                        }} 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>
+                        {mode === 'total' && <TimePicker disabledTime={disabledTime} size='small' style={{ width: 110 }} onChange={(e) => { setHour(e ? moment(e).format('H') : null) }} format="HH" />}
+                        <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 : mode === 'detail' ? getPlanDetailList?.data?.data?.length : getMinuteList?.data?.data?.length}
+                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({ data: order ? getPlanDetailList?.data?.data?.sort(compare(field, order)) : [...planDetailList] })
+                        } else if (mode === 'minute') {
+                            getMinuteList?.mutate({ data: order ? getMinuteList?.data?.data?.sort(compare(field, order)) : [...minuteList] })
+                        }
+                    }
+                }}
+                page={mode === 'total' ? queryForm.pageNum : undefined}
+                pageSize={mode === 'total' ? queryForm.pageSize : undefined}
+                scroll={{ y: 750 }}
+                config={mode === 'minute' ? qiliangpaihangminute : mode === 'detail' ? qiliangpaihanghour : qiliangpaihang}
+                configName={mode === 'total' ? '起量广告排行' : mode === 'detail' ? '起量广告排行明细' : '起量广告5min'}
+            />
+        </div>
+
+        {visible && <PlanDetail visible={visible} onClose={() => { setVisible(false) }} data={aId} />}
+    </Space>
+}
+
+
+export default React.memo(Monitor)

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

@@ -0,0 +1,223 @@
+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 { 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 { getPicherList } = useModel('useOperating.useWxGroupList')
+    // 变量开始
+    const [queryForm, setQueryForm] = useState<allPlanProps>({ pageNum: 1, pageSize: 100, sysUserId: JSON.parse(sessionStorage.getItem('SYSUSERID') || '[]'), createStartTime: moment().subtract(3, 'days').format('YYYY-MM-DD'), createEndTime: moment().format('YYYY-MM-DD') }) // 搜索变量
+    const [visible, setVisible] = useState<boolean>(false) // 详情弹窗控制
+    const [adId, setAdId] = useState<any>()
+    const [downLoadLoading, setDownLoadLoading] = useState<boolean>(false)
+    // 变量结束
+
+    // 获取广告账号
+    useEffect(() => {
+        getAdqAccountList.run()
+    }, [])
+
+    // 获取投手
+    useEffect(() => {
+        getPicherList.run()
+    }, [])
+
+    useEffect(() => {
+        if (sessionStorage.getItem('ADIDORNAME')) {
+            let data = JSON.parse(sessionStorage.getItem('ADIDORNAME') as any)
+            setQueryForm({ ...queryForm, createStartTime: undefined, createEndTime: undefined, adgroup: data.adgroupId, campaign: data.campaignId, sysUserId: data.putUserId ? data.putUserId?.toString()?.split() : [], 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))
+        downLoadAllAdListApi(newQueryForm).then(res => {
+            setDownLoadLoading(false)
+            downloadFile1(res, 'octet-stream', formatDate(new Date()) + ".xlsx")
+        }).catch(() => setDownLoadLoading(false))
+    }, [queryForm, 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 bodyStyle={{ padding: '12px 16px' }}>
+            <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}
+                        mode='multiple'
+                        maxTagCount={1}
+                        style={{ minWidth: 150 }}
+                        allowClear
+                        placeholder="请选择投手"
+                        onChange={(value: any[]) => { sessionStorage.setItem('SYSUSERID', value ? JSON.stringify(value) : ''); setQueryForm({ ...queryForm, pageNum: 1, sysUserId: value }) }}
+                    >
+                        {getPicherList?.data?.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="请选择广告账号"
+                        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}
+                        </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?.creativeId} onChange={(e) => { setQueryForm({ ...queryForm, pageNum: 1, creativeId: 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
+            bodyStyle={{ padding: '12px 16px' }}
+            gutter={[0, 12]}
+            columns={columnsPlanList(detail)}
+            ajax={getAllPlanList}
+            dataSource={getAllPlanList?.data?.data?.records}
+            loading={getAllPlanList?.loading}
+            scroll={{ y: 750 }}
+            fixed={{ left: 0, right: 1 }}
+            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;
+    }
+  }
+}
+
+

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

@@ -0,0 +1,698 @@
+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, CHUJIAFANGSHI, GOUMAILEIXING, GUANGGAOZHUANGTAI, TUIGUANGMUBIAO, YOUHUAMUBIAO } from './enum'
+import Box from './components/box'
+import { GGStateData } from './data'
+function columnsMonitor(planDetail: (id: number) => void, getDetailList: (adId: any, accountId: any[]) => void, details: (id: number) => void, getMinuList: (id: number, accountId: any[]) => void, mode: string) {
+    const { copy } = useCopy()
+    return function columns() {
+        let newArr: ColumnsType<any> = [
+            {
+                title: '时间',
+                dataIndex: 'currTime',
+                key: 'currTime',
+                align: 'center',
+                width: 160,
+                ellipsis: true
+            },
+            {
+                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, [b.accountId]) }}>{str}</a>
+                }
+            },
+            {
+                title: '广告ID',
+                dataIndex: 'adgroupId',
+                key: 'adgroupId',
+                align: 'center',
+                width: 100,
+                ellipsis: true,
+                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,
+                ellipsis: true,
+                render: (a: any) => {
+                    return <a onClick={() => { copy(a) }} style={{ color: '#3946c3' }}>{a}</a>
+                }
+            },
+            {
+                title: '广告账户',
+                dataIndex: 'accountId',
+                key: 'accountId',
+                width: 70,
+                ellipsis: true,
+                align: 'center'
+            },
+            {
+                title: '投手',
+                dataIndex: 'putUserName',
+                key: 'putUserName',
+                align: 'center',
+                ellipsis: true,
+                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',
+                ellipsis: true
+            },
+            {
+                title: '广告状态',
+                dataIndex: 'adStatus',
+                key: 'adStatus',
+                align: 'center',
+                width: 105,
+                ellipsis: true,
+                render: (a: any) => {
+                    return GGStateData[a] || '--'
+                }
+            },
+            // {
+            //     title: '创意状态',
+            //     dataIndex: 'adCreativeStatus',
+            //     key: 'adCreativeStatus',
+            //     align: 'center',
+            //     width: 115,
+            //     render: (a: any) => {
+            //         return CHUANGYIZHUANGTAI[a] || '--'
+            //     }
+            // },
+            {
+                title: '推广目标',
+                dataIndex: 'promotedObjectType',
+                key: 'promotedObjectType',
+                align: 'center',
+                width: 80,
+                ellipsis: true,
+                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',
+                ellipsis: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '出价方式',
+                dataIndex: 'bidMode',
+                key: 'bidMode',
+                align: 'center',
+                width: 80,
+                render: (a: any) => {
+                    return CHUJIAFANGSHI[a] || '--'
+                }
+            },
+            {
+                title: '当前出价',
+                dataIndex: 'bidAmount',
+                key: 'bidAmount',
+                width: 110,
+                align: 'center',
+                ellipsis: true,
+                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,
+                ellipsis: true,
+                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, [b.accountId]) }}>小时</a>
+                            </Menu.Item> : <Menu.Item key="1">
+                                <a style={{ color: '#1890ff' }} onClick={() => { getMinuList(b?.adgroupId, [b.accountId]) }}>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.Item key="3">
+                                <a style={{ color: '#1890ff' }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.accountId}/admanage/adcreative?adgroupid=${b?.adgroupId}&query={%22configured_status%22:[%22AD_STATUS_EXCLUDE_DELETED%22],%22status%22:[]}`)} target="_blank">腾讯广告</a>
+                            </Menu.Item>
+                        </Menu>
+                    } trigger={['click']}>
+                        <a className="ant-dropdown-link" onClick={e => e.preventDefault()}>
+                            更多 <DownOutlined />
+                        </a>
+                    </Dropdown>
+                )
+            }
+        ]
+        return newArr
+    }
+}
+export default columnsMonitor

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

@@ -0,0 +1,554 @@
+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, CHUJIAFANGSHI } from './enum'
+import Box from './components/box'
+import { GGStateData } from './data'
+import { copy } from '@/utils/utils'
+function columnsMonitor(details: (id: number) => void) {
+    return function columns() {
+        let newArr: any = [
+            {
+                title: '数据日期',
+                dataIndex: 'beginToEnd',
+                key: 'beginToEnd',
+                align: 'center',
+                width: 100,
+                fixed: 'left',
+                className: 'padding2',
+                render: (str: any, b: any) => {
+                    return <div style={{ lineHeight: 'normal', fontSize: 10 }}>{str}</div>
+                }
+            },
+            {
+                title: '广告名称/ID',
+                dataIndex: 'adgroupName',
+                key: 'adgroupName',
+                align: 'left',
+                width: 210,
+                className: 'padding2',
+                ellipsis: true,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', transform: 'scale(0.95)' }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }} className='oneText' onClick={() => copy(b.adgroupName)}>{b.adgroupName}</div>
+                        <div style={{ color: '#888', fontSize: 10 }}> 广告ID:{b?.adgroupId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '投放计划名称/ID',
+                dataIndex: 'campaignName/campaignId',
+                key: 'campaignName/campaignId',
+                align: 'left',
+                width: 180,
+                className: 'padding2',
+                ellipsis: true,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', transform: 'scale(0.95)' }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }} className='oneText' onClick={() => copy(b.campaignName)}>{b.campaignName}</div>
+                        <div style={{ color: '#888', fontSize: 10 }}> 计划ID:{b?.campaignId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '创意名称/ID',
+                dataIndex: 'creativeName/creativeId',
+                key: 'creativeName/creativeId',
+                align: 'left',
+                className: 'padding2',
+                width: 210,
+                render: (str: any, b: any) => {
+                    return <div style={{ display: 'flex', flexFlow: 'column nowrap', transform: 'scale(0.95)' }}>
+                        <div style={{ color: '#459ae9', fontSize: 10 }} className='oneText' onClick={() => copy(b.creativeName)}>{b.creativeName}</div>
+                        <div style={{ color: '#888', fontSize: 10 }}> 创意ID:{b?.creativeId}</div>
+                    </div>
+                }
+            },
+            {
+                title: '创意预览',
+                dataIndex: 'creativePreview',
+                key: 'creativePreview',
+                width: 110,
+                align: 'center',
+                render: (a: any, b: any) => {
+                    // return a && a?.length > 0 ? <a href={a[0]} target='_blank'>预览</a> : '--'
+                    return <Box b={b} />
+                }
+            },
+            {
+                title: '广告账户',
+                dataIndex: 'accountId',
+                key: 'accountId',
+                width: 70,
+                align: 'center'
+            },
+            {
+                title: '投手',
+                dataIndex: 'putUserName',
+                key: 'putUserName',
+                width: 70,
+                align: 'center',
+                render: (a: any) => {
+                    return a || '--'
+                }
+            },
+            {
+                title: '创建日期',
+                dataIndex: 'adCreateTime',
+                key: 'adCreateTime',
+                align: 'center',
+                width: 110,
+                className: 'padding2',
+                render: (str: any, b: any) => {
+                    return <div style={{ lineHeight: 'normal', fontSize: 10 }}>{str}</div>
+                }
+            },
+            {
+                title: '广告状态',
+                dataIndex: 'adStatus',
+                key: 'adStatus',
+                align: 'center',
+                width: 105,
+                ellipsis: true,
+                render: (a: any) => {
+                    return GGStateData[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,
+                className: 'padding2',
+                render: (a: any, b: any) => {
+                    return a ? <div style={{ display: 'flex', flexFlow: 'column nowrap', lineHeight: 'normal', fontSize: 10 }}>
+                        <span>{a}</span>
+                        {/* <span>{b?.adEndDate}</span> */}
+                    </div> : '--'
+                }
+            },
+            // {
+            //     title: '当日成本偏差',
+            //     dataIndex: 'dayCostOffset',
+            //     key: 'dayCostOffset',
+            //     align: 'center',
+            //     width: 90,
+            //     render: (a: any) => {
+            //         return a || '--'
+            //     }
+            // },
+            {
+                title: '推广目标',
+                dataIndex: 'promotedObjectType',
+                key: 'promotedObjectType',
+                align: 'center',
+                width: 80,
+                ellipsis: true,
+                render: (a: any) => {
+                    return TUIGUANGMUBIAO[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 CHUJIAFANGSHI[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: 'cost',
+                key: 'cost',
+                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,
+                className: 'padding2',
+                render: (a: any, b: any) => {
+                    return <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', lineHeight: 'normal', fontSize: 10 }}>
+                        {/* <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) => {
+                    return <span style={a <= 8 ? { color: '#0f990f', fontWeight: 600 } : a >= 100 ? { color: 'red', fontWeight: 600 } : {}}> {a || '--'}</span >
+                },
+            },
+            {
+                title: '千次曝光成本',
+                dataIndex: 'thousandDisplayPrice',
+                key: 'thousandDisplayPrice',
+                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) => {
+                    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: 'noInterestCount',
+                key: 'noInterestCount',
+                align: 'center',
+                width: 70,
+                sorter: true,
+                render: (a: number) => {
+                    return <span style={a <= 8 ? { color: '#0f990f', fontWeight: 600 } : a >= 100 ? { color: 'red', fontWeight: 600 } : {}}> {a || '--'}</span >
+                },
+            },
+            {
+                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: 'deepConversionsCount',
+                key: 'deepConversionsCount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '下单量',
+                dataIndex: 'orderCount',
+                key: 'orderCount',
+                align: 'center',
+                width: 115,
+                sorter: true,
+                render: (a: any) => {
+                    return <Statistic value={a || 0} />
+                }
+            },
+            {
+                title: '首日新增下单量',
+                dataIndex: 'firstDayOrderCount',
+                key: 'firstDayOrderCount',
+                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: 'firstDayOrderAmount',
+                key: 'firstDayOrderAmount',
+                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: 'firstDayOrderRoi',
+                key: 'firstDayOrderRoi',
+                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} />
+                }
+            },
+            {
+                title: '操作',
+                dataIndex: 'event',
+                key: 'event',
+                align: 'center',
+                width: 90,
+                render: (a: any, b: any) => {
+                    return <a style={{ color: '#1890ff' }} onClick={() => window.open(`https://ad.qq.com/atlas/${b?.accountId}/admanage/adcreative?adgroupid=${b?.adgroupId}&query={%22configured_status%22:[%22AD_STATUS_EXCLUDE_DELETED%22],%22status%22:[]}`)} target="_blank">腾讯广告</a>
+                }
+            }
+        ]
+        return newArr
+    }
+}
+export default columnsMonitor
+

+ 2 - 3
src/pages/launchSystemNew/adq/index.tsx

@@ -181,9 +181,8 @@ function Adq() {
                         placeholder='广点通账号'
                         style={{ minWidth: 200 }}
                         showSearch
-                        filterOption={(input: any, option: any) =>
-                            (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
-                        } allowClear onChange={(value: any) => {
+                        allowClear 
+                        onChange={(value: any) => {
                             let accountId = ''
                             let adAccountId = ''
                             if (value) {

+ 15 - 8
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -40,10 +40,16 @@ interface Prosp {
     hoverable?: boolean,
     rowSelection?: any,
     myKey?: any,//自定义使用哪个值做key
+    fixed?: {
+        left: number,
+        right: number
+    },
+    bodyStyle?: any
+    gutter?: any
 }
 
 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, gutter= [0, 20] } = props
     const { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -51,7 +57,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 +78,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 +90,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 +216,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 +231,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>
@@ -236,9 +242,10 @@ function TableData(props: Prosp) {
             <Card
                 hoverable={hoverable}
                 title={title}
+                bodyStyle={props?.bodyStyle}
                 headStyle={{ textAlign: 'left' }}
             >
-                <Row gutter={[0, 20]}>
+                <Row gutter={gutter}>
                     {header}
                     <Tab {...{ size, newColumns, handelResize, className, isZj, rowSelection, columns, loading, scroll, isFell, page, pageSize, dataSource, onChange, expandedRowRender, total, ajax, myKey }} />
                 </Row>
@@ -258,7 +265,7 @@ const Tab = React.memo((props: any) => {
             {dataSource || !ajax?.loading ? <Tables
                 className={`all_table ${className ? className : ''}`}
                 bordered
-                sortDirections={['ascend', 'descend', null]}
+                sortDirections={['descend', 'ascend', null]}
                 current={page}
                 pageSize={pageSize}
                 columns={newColumns?.length > 0 ? newColumns : columns()}

+ 43 - 765
src/pages/launchSystemNew/components/addLandingPage/content.ts

@@ -1,4 +1,4 @@
-import { Img, TopImg, TopSlider, TopVideo, Text, GhButton, WxAutoButton, Floatbutton } from "../../req"
+import { Img, TopImg, TopSlider, TopVideo, Text, GhButton, WxAutoButton, Floatbutton, ITItemGhSpec, ITItemEnterpriseWxSpec, ImageText, ImageTextItem } from "../../req"
 
 // 头部内容
 export const topsliderContent: TopSlider = {
@@ -66,37 +66,6 @@ export const floatbuttonBtTypeGh: Omit<GhButton, 'btnBorderColorTheme' | 'useIco
     fastFollow: 1,
 }
 
-export const topvideoContent = {
-    id: 'widget_1625551322566_1',
-    widgetTypeV2: 'topvideo',
-    widgetType: 'top',
-    name: "顶部视频",
-    type: '61',
-    viewType: '0',
-    styleType: '0',
-    initWidth: 750,
-    initHeight: 750,
-    paddingTop: 0,
-    paddingBottom: 0,
-    paddingLeft: 0,
-    paddingRight: 0,
-    sightDisplayWidth: 0,
-    sightDisplayHeight: 0,
-    sightThumbUrl: '',
-    sightVideoUrl: '',
-    sightDisplayType: '0',
-    sightThumbMd5: '',
-    sightVideoMd5: '',
-    isFullScreen: '0',
-    videoDuration: '',
-    BitRate: 0,
-    video_name: 'topVideo',
-    video_type: 'mp4',
-    adLocation: 'sns',
-    fileSize: 0,
-    materialId: ''
-}
-
 // 内容
 export const imgContent: Img = {
     elementType: 'IMAGE',
@@ -131,34 +100,6 @@ export const ghContent: GhButton = {
     paddingBottom: 28,
 }
 
-export const linkContent = {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    type: "21",
-    name: "跳转链接",
-    btnTitle: "了解详情",
-    subType: "0",
-    fontSize: "30",
-    fontColor: "#FFFFFF",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "80",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: 28,
-    paddingBottom: 28,
-    paddingRight: 185,
-    paddingLeft: 185,
-    cornerRadius: 4,
-    useIcon: "0",
-    id: "widget_1632641482474_3",
-    mpJumpType: "1",
-    origBtnJumpUrl: "",
-    btnJumpUrl: ""
-}
-
 // 添加商家微信
 export const wxAutoContent: WxAutoButton = {
     elementType: 'ENTERPRISE_WX',
@@ -172,717 +113,54 @@ export const wxAutoContent: WxAutoButton = {
     fontColor: "#FFFFFF",
 }
 
-// 图文复合组件104
-export const shelfnewContent = {
-    widgetType: "shelfnew",
-    widgetTypeV2: "shelfnew",
-    name: "图文复合组件",
-    type: "104",
-    id: "widget_1638426338182_2",
-    layoutHeight: "228",
-    layoutWidth: "670",
-    paddingTop: 20,
-    paddingBottom: 20,
-    paddingLeft: 40,
-    paddingRight: 40,
-    borderColor: "#e5e5e5",
-    bgColor: "#ffffff",
-    borderSize: 1,
-    cornerRadius: 8,
-    isChooseJump: 1,
-    layoutItems: {
-        componentItem: [
-            {
-                layoutHeight: "228",
-                layoutWidth: "670",
-                type: "103",
-                subType: "1",
-                jumpMode: "btn_jump",
-                layoutItems: {
-                    componentItem: [
-                        {
-                            name: "图片",
-                            layoutHeight: "180",
-                            layoutWidth: "180",
-                            paddingTop: "24",
-                            paddingLeft: "24",
-                            pureImageUrl: "",  // 图片url
-                            type: "41",
-                            imageMd5: "",   // Md5
-                            cornerRadius: "4",
-                            materialId: '',
-                            wxad_IsSubNode: "1"
-                        },
-                        {
-                            layoutHeight: "228",
-                            layoutWidth: "470",
-                            paddingLeft: "24",
-                            paddingTop: "24",
-                            paddingRight: "40",
-                            subType: "0",
-                            type: "103",
-                            descType: "text",
-                            layoutItems: {
-                                componentItem: [
-                                    {
-                                        name: "标题",
-                                        content: "",  // 标题001
-                                        fontColor: "#353535",
-                                        fontSize: "32",
-                                        layoutHeight: "44",
-                                        layoutWidth: "410",
-                                        maxLines: "1",
-                                        showType: "1",
-                                        type: "1",
-                                        wxad_IsSubNode: "1"
-                                    },
-                                    {
-                                        name: "描述",
-                                        content: "",  // 文字描述001
-                                        fontColor: "#b2b2b2",
-                                        fontSize: "24",
-                                        layoutHeight: "40",
-                                        layoutWidth: "410",
-                                        paddingTop: "8",
-                                        type: "1",
-                                        wxad_IsSubNode: "1"
-                                    },
-                                    {
-                                        widgetTypeV2: "link",
-                                        widgetType: "button",
-                                        borderSize: "0",
-                                        btnBgColorTheme: "#07C160",
-                                        btnBorderColorTheme: "#FFFFFF",
-                                        btnFontType: "0",
-                                        btnHeight: "60",
-                                        btnJumpUrl: "",
-                                        btnStyle: "1",
-                                        btnTitle: "了解详情",
-                                        btnType: "0",
-                                        fontColor: "#FFFFFF",
-                                        fontSize: "26",
-                                        name: "跳转链接",
-                                        origBtnJumpUrl: "",
-                                        layoutWidth: "152",
-                                        layoutHeight: "60",
-                                        paddingBottom: "0",
-                                        paddingLeft: "0",
-                                        paddingRight: "0",
-                                        paddingTop: "28",
-                                        subType: "0",
-                                        mpJumpType: "1",
-                                        type: "21",
-                                        cornerRadius: "4"
-                                    }
-                                ]
-                            },
-                            wxad_IsSubNode: "1"
-                        }
-                    ]
-                }
-            }
-        ]
-    }
-}
-
-// 图文复合组件103
-export const shelfnewContent2 = {
-    widgetTypeV2: "shelfnew",
-    widgetType: "shelfnew",
-    name: "图文复合组件",
-    type: "103",
-    id: "widget_1638501500690_1",
-    layoutHeight: "542",
-    paddingLeft: "40",
-    paddingTop: "20",
-    paddingBottom: "20",
-    subType: "1",
-    isChooseJump: 1,
-    wxad_align: 0,
-    layoutItems: {
-        componentItem: [
-            {
-                layoutHeight: "542",
-                layoutWidth: "324",
-                type: "104",
-                borderColor: "#E5E5E5",
-                bgColor: "#FFFFFF",
-                borderSize: "1",
-                cornerRadius: "8",
-                layoutItems: {
-                    componentItem: [{
-                        jumpMode: "btn_jump",
-                        layoutHeight: "542",
-                        layoutWidth: "324",
-                        type: "103",
-                        subType: "0",
-                        descType: "text",
-                        layoutItems: {
-                            componentItem: [{
-                                name: "图片",
-                                layoutHeight: "300",
-                                pureImageUrl: "",
-                                type: "41",
-                                imageMd5: "",
-                                layoutWidth: "300",
-                                paddingLeft: "12",
-                                paddingTop: "12",
-                                cornerRadius: "4",
-                                materialId: '',
-                                wxad_IsSubNode: "1"
-                            }, {
-                                name: "标题",
-                                content: "",
-                                fontColor: "#353535",
-                                fontSize: "32",
-                                layoutHeight: "44",
-                                layoutWidth: "272",
-                                maxLines: "1",
-                                paddingLeft: "24",
-                                paddingTop: "16",
-                                showType: "1",
-                                type: "1",
-                                wxad_IsSubNode: "1"
-                            }, {
-                                name: "描述",
-                                content: "",
-                                fontColor: "#4D4D4D",
-                                fontSize: "24",
-                                layoutHeight: "40",
-                                layoutWidth: "272",
-                                paddingTop: "8",
-                                type: "1",
-                                wxad_IsSubNode: "1",
-                                paddingLeft: "24"
-                            }, {
-                                widgetTypeV2: "gh",
-                                widgetType: "button",
-                                type: "21",
-                                name: "商品关注公众号",
-                                btnTitle: "关注公众号",
-                                subType: "17",
-                                fontSize: "26",
-                                fontColor: "#FFFFFF",
-                                btnType: "0",
-                                borderSize: "0",
-                                btnHeight: "60",
-                                btnBgColorTheme: "#07C160",
-                                btnBorderColorTheme: "#FFFFFF",
-                                btnFontType: "0",
-                                btnStyle: "1",
-                                paddingTop: "28",
-                                paddingBottom: "0",
-                                paddingRight: "24",
-                                paddingLeft: "24",
-                                cornerRadius: 4,
-                                useIcon: "0",
-                                field21_1: {
-                                    origBtnJumpUrl: "",
-                                    wxad_guide_group_id: ""
-                                },
-                                id: "widget_1638502917231_4",
-                                layoutHeight: "60",
-                                layoutWidth: "152"
-                            }]
-                        }
-                    }]
-                },
-                wxad_IsSubNode: "1"
-            },
-            {
-                layoutHeight: "542",
-                layoutWidth: "324",
-                type: "104",
-                borderColor: "#E5E5E5",
-                bgColor: "#FFFFFF",
-                borderSize: "1",
-                cornerRadius: "8",
-                paddingLeft: '22',
-                layoutItems: {
-                    componentItem: [{
-                        jumpMode: "btn_jump",
-                        layoutHeight: "542",
-                        layoutWidth: "324",
-                        type: "103",
-                        subType: "0",
-                        descType: "text",
-                        layoutItems: {
-                            componentItem: [{
-                                name: "图片",
-                                layoutHeight: "300",
-                                pureImageUrl: "",
-                                type: "41",
-                                imageMd5: "",
-                                layoutWidth: "300",
-                                paddingLeft: "12",
-                                paddingRight: "12",
-                                paddingTop: "12",
-                                cornerRadius: "4",
-                                materialId: '',
-                                wxad_IsSubNode: "1"
-                            }, {
-                                name: "标题",
-                                content: "",
-                                fontColor: "#353535",
-                                fontSize: "32",
-                                layoutHeight: "44",
-                                layoutWidth: "272",
-                                maxLines: "1",
-                                paddingLeft: "24",
-                                paddingTop: "16",
-                                showType: "1",
-                                type: "1",
-                                wxad_IsSubNode: "1"
-                            }, {
-                                name: "描述",
-                                content: "",
-                                fontColor: "#4D4D4D",
-                                fontSize: "24",
-                                layoutHeight: "40",
-                                layoutWidth: "272",
-                                paddingLeft: "24",
-                                paddingTop: "8",
-                                type: "1",
-                                wxad_IsSubNode: "1"
-                            }, {
-                                widgetTypeV2: "gh",
-                                widgetType: "button",
-                                type: "21",
-                                name: "商品关注公众号",
-                                btnTitle: "关注公众号",
-                                subType: "17",
-                                fontSize: "26",
-                                fontColor: "#FFFFFF",
-                                btnType: "0",
-                                borderSize: "0",
-                                btnHeight: "60",
-                                btnBgColorTheme: "#07C160",
-                                btnBorderColorTheme: "#FFFFFF",
-                                btnFontType: "0",
-                                btnStyle: "1",
-                                paddingTop: "28",
-                                paddingBottom: "0",
-                                paddingRight: "24",
-                                paddingLeft: "24",
-                                cornerRadius: 4,
-                                useIcon: "0",
-                                field21_1: {
-                                    origBtnJumpUrl: "",
-                                    wxad_guide_group_id: ""
-                                },
-                                id: "widget_1638502917231_4",
-                                layoutHeight: "60",
-                                layoutWidth: "152"
-                            }]
-                        }
-                    }]
-                },
-                wxad_IsSubNode: "1"
-            }]
-    }
-}
-
-// 图文复合组件按钮类型参数
-// 单个
-// 跳转链接 104
-export const jumpLink104 = {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "0",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnHeight: "60",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: "了解详情",
-    btnType: "0",
-    fontColor: "#FFFFFF",
-    fontSize: "26",
-    name: "跳转链接",
-    origBtnJumpUrl: "",
-    layoutWidth: "152",
-    layoutHeight: "60",
-    paddingBottom: "0",
-    paddingLeft: "0",
-    paddingRight: "0",
-    paddingTop: "28",
-    subType: "0",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-// 跳转链接 103
-export const jumpLink103 = {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "0",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnHeight: "60",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: "了解详情",
-    btnType: "0",
-    fontColor: "#FFFFFF",
-    fontSize: "26",
-    name: "跳转链接",
-    origBtnJumpUrl: "",
-    layoutWidth: "152",
-    layoutHeight: "60",
-    paddingBottom: "32",
-    paddingLeft: "24",
-    paddingRight: "24",
-    paddingTop: "28",
-    subType: "0",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-
-// 关注公众号 104
-export const jumpGh104 = {
-    widgetTypeV2: "gh",
-    widgetType: "button",
-    type: "21",
-    name: "商品关注公众号",
-    btnTitle: "关注公众号",
-    subType: "17",
-    fontSize: "26",
-    fontColor: "#FFFFFF",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "60",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "28",
-    paddingBottom: "0",
-    paddingRight: "0",
-    paddingLeft: "0",
-    cornerRadius: 4,
-    useIcon: "0",
-    field21_1: {
-        origBtnJumpUrl: "",
-        wxad_guide_group_id: ""
-    },
-    id: "widget_1634536343386_3",
-    layoutWidth: "152",
-    layoutHeight: "60"
-}
-// 关注公众号 103
-export const jumpGh103 = {
-    widgetTypeV2: "gh",
-    widgetType: "button",
-    type: "21",
-    name: "商品关注公众号",
-    btnTitle: "关注公众号",
-    subType: "17",
-    fontSize: "26",
-    fontColor: "#FFFFFF",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "60",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "28",
-    paddingBottom: "0",
-    paddingRight: "24",
-    paddingLeft: "24",
-    cornerRadius: 4,
-    useIcon: "0",
-    field21_1: {
-        origBtnJumpUrl: "",
-        wxad_guide_group_id: ""
-    },
-    id: "widget_1638502917231_4",
-    layoutHeight: "60",
-    layoutWidth: "152"
+/** 图文复合组件 默认值 */
+export const ghITItemContent: ITItemGhSpec = {
+    btnTitle: '关注公众号',
+    fontColor: '#FFFFFF',
+    btnBgColorTheme: '#07C160',
+    btnBorderColorTheme: '#FFFFFF',
+    btnFontType: 0,
+    useIcon: 0,
+    fastFollow: 1,
+    paddingTop: 28,
+    paddingBottom: 28,
 }
 
-// 商品添加商家微信 104
-export const jumpWxAuto104 = {
-    widgetTypeV2: "enterprise_wx_auto",
-    widgetType: "button",
-    type: "21",
-    name: "商品添加商家微信",
-    btnTitle: "联系商家",
-    subType: "15",
-    fontSize: "26",
-    fontColor: "#FFFFFF",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "60",
+// 添加商家微信
+export const wxAutoITItemContent: ITItemEnterpriseWxSpec = {
     btnBgColorTheme: "#07C160",
     btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "28",
-    paddingBottom: "0",
-    paddingRight: "0",
-    paddingLeft: "0",
-    cornerRadius: 4,
-    useIcon: "0",
-    mpJumpType: "1",
-    origBtnJumpUrl: "",//"https://wxa.wxs.qq.com/mptemplate/rtx/index.html?corpid=wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g&groupid=215982&_=1646277580091",
-    btnJumpUrl: "",
-    corpid: "",//"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g",
-    groupid: null,//215982,
-    h5_config_id: "",//"6eeeabc55c3087ea251505ec8560371e",
-    wxapp_config_id: "",//"bee1788a8cbf0bbab69d3bfc5917231e",
-    qrUrl: "https://wework.qpic.cn/wwpic/87591_JQGte4L-Tc21VZW_1645432835/0",
-    needUpdateQrUrl: 0,
-    qrExtInfo: "",//"{\"qrType\":1,\"corpid\":\"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g\",\"groupid\":215982,\"useSet\":0}",
-    serviceType: "1",
-    chatGroupName: "", //"单人客服-木木",
-    id: "widget_1646277501989_6",
-    layoutWidth: "152",
-    layoutHeight: "60"
-}
-// 商品添加商家微信 103
-export const jumpWxAuto103 = {
-    widgetTypeV2: "enterprise_wx_auto",
-    widgetType: "button",
-    type: "21",
-    name: "商品添加商家微信",
+    btnFontType: 0,
     btnTitle: "联系商家",
-    subType: "15",
-    fontSize: "26",
+    useIcon: 0,
+    paddingTop: 28,
+    paddingBottom: 28,
     fontColor: "#FFFFFF",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "60",
-    btnBgColorTheme: "#07C160",
-    btnBorderColorTheme: "#FFFFFF",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "28",
-    paddingBottom: "0",
-    paddingRight: "24",
-    paddingLeft: "24",
-    cornerRadius: 4,
-    useIcon: "0",
-    mpJumpType: "1",
-    origBtnJumpUrl: "",//"https://wxa.wxs.qq.com/mptemplate/rtx/index.html?corpid=wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g&groupid=215982&_=1646279359946",
-    btnJumpUrl: "",
-    corpid: "",//"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g",
-    groupid: null, //215982,
-    h5_config_id: "", //"6eeeabc55c3087ea251505ec8560371e",
-    wxapp_config_id: "",//"bee1788a8cbf0bbab69d3bfc5917231e",
-    qrUrl: "https://wework.qpic.cn/wwpic/87591_JQGte4L-Tc21VZW_1645432835/0",
-    needUpdateQrUrl: 0,
-    qrExtInfo: "", //"{\"qrType\":1,\"corpid\":\"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g\",\"groupid\":215982,\"useSet\":0}",
-    serviceType: "1",
-    chatGroupName: "",//"单人客服-木木",
-    id: "widget_1646279296521_3",
-    layoutHeight: "60",
-    layoutWidth: "152"
 }
 
-// 全局跳转 跳转链接 104
-export const btModelJumpLink = {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "0",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnHeight: "228",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: "",
-    btnType: "0",
-    fontColor: "clear",
-    fontSize: "26",
-    name: "跳转链接",   // 
-    origBtnJumpUrl: "",
-    layoutWidth: "750",
-    layoutHeight: "228",
-    paddingBottom: "0",
-    paddingLeft: "0",
-    paddingRight: "0",
-    paddingTop: "0",
-    subType: "0",
-    fengyeId: "0",
-    fengyeUrl: "",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-// 全局跳转 跳转链接 103
-export const btModelJumpLink103 = {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "0",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnHeight: "542",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: "",
-    btnType: "0",
-    fontColor: "clear",
-    fontSize: "26",
-    name: "跳转链接",
-    origBtnJumpUrl: "",
-    layoutWidth: "320",
-    layoutHeight: "542",
-    paddingBottom: "32",
-    paddingLeft: "30",
-    paddingRight: "24",
-    paddingTop: "0",
-    subType: "0",
-    fengyeId: "0",
-    fengyeUrl: "",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-// 全局跳转 关注公众号 104
-export const btModelJumpGh = {
-    widgetTypeV2: "gh",
-    widgetType: "button",
-    type: "21",
-    name: "商品关注公众号",
-    btnTitle: "",
-    subType: "17",
-    wxad_guide_group_status: false,
-    fontSize: "26",
-    fontColor: "clear",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "228",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "0",
-    paddingBottom: "0",
-    paddingRight: "0",
-    paddingLeft: "0",
-    cornerRadius: 4,
-    useIcon: "0",
-    field21_1: {
-        origBtnJumpUrl: "",
-        wxad_guide_group_id: ""
-    },
-    id: "widget_1634288817126_3",
-    layoutWidth: "750",
-    layoutHeight: "228"
-}
-// 全局跳转 关注公众号 103
-export const btModelJumpGh103 = {
-    widgetTypeV2: "gh",
-    widgetType: "button",
-    type: "21",
-    name: "商品关注公众号",
-    btnTitle: "",
-    subType: "17",
-    wxad_guide_group_status: false,
-    fontSize: "26",
-    fontColor: "clear",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "542",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "0",
-    paddingBottom: "0",
-    paddingRight: "24",
-    paddingLeft: "24",
-    cornerRadius: 4,
-    useIcon: "0",
-    field21_1: {
-        origBtnJumpUrl: "",
-        wxad_guide_group_id: ""
-    },
-    id: "widget_1638759421426_2",
-    layoutHeight: "542",
-    layoutWidth: "320"
-}
-
-// 全局跳转 商品添加商家微信 104
-export const btModelJumpWxAuto = {
-    widgetTypeV2: "enterprise_wx_auto",
-    widgetType: "button",
-    type: "21",
-    name: "商品添加商家微信",
-    btnTitle: "",
-    subType: "15",
-    fontSize: "26",
-    fontColor: "clear",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "228",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "0",
-    paddingBottom: "0",
-    paddingRight: "0",
-    paddingLeft: "0",
-    cornerRadius: 4,
-    useIcon: "0",
-    mpJumpType: "1",
-    origBtnJumpUrl: "",//"https://wxa.wxs.qq.com/mptemplate/rtx/index.html?corpid=wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g&groupid=215982&_=1646277580092",
-    btnJumpUrl: "",
-    corpid: "",//"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g",
-    groupid: null,//215982,
-    h5_config_id: "",//"6eeeabc55c3087ea251505ec8560371e",
-    wxapp_config_id: "", //"bee1788a8cbf0bbab69d3bfc5917231e",
-    qrUrl: "https://wework.qpic.cn/wwpic/87591_JQGte4L-Tc21VZW_1645432835/0",
-    needUpdateQrUrl: 0,
-    qrExtInfo: "", //"{\"qrType\":1,\"corpid\":\"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g\",\"groupid\":215982,\"useSet\":0}",
-    serviceType: "1",
-    chatGroupName: "", //"单人客服-木木",
-    id: "",  //"widget_1646277501989_6",
-    layoutWidth: "750",
-    layoutHeight: "228"
-}
-// 全局跳转 商品添加商家微信 103
-export const btModelJumpWxAuto103 = {
-    widgetTypeV2: "enterprise_wx_auto",
-    widgetType: "button",
-    type: "21",
-    name: "商品添加商家微信",
-    btnTitle: "",
-    subType: "15",
-    fontSize: "26",
-    fontColor: "clear",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "542",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "0",
-    paddingBottom: "0",
-    paddingRight: "24",
-    paddingLeft: "24",
-    cornerRadius: 4,
-    useIcon: "0",
-    mpJumpType: "1",
-    origBtnJumpUrl: "",//"https://wxa.wxs.qq.com/mptemplate/rtx/index.html?corpid=wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g&groupid=215982&_=1646279359946",
-    btnJumpUrl: "",
-    corpid: "",//"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g",
-    groupid: null,//215982,
-    h5_config_id: "",//"6eeeabc55c3087ea251505ec8560371e",
-    wxapp_config_id: "", //"bee1788a8cbf0bbab69d3bfc5917231e",
-    qrUrl: "https://wework.qpic.cn/wwpic/87591_JQGte4L-Tc21VZW_1645432835/0",
-    needUpdateQrUrl: 0,
-    qrExtInfo: "", //"{\"qrType\":1,\"corpid\":\"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g\",\"groupid\":215982,\"useSet\":0}",
-    serviceType: "1",
-    chatGroupName: "", //"单人客服-木木",
-    id: "", //"widget_1646279296521_3",
-    layoutHeight: "542",
-    layoutWidth: "320"
+export const imageTextItem: Omit<ImageTextItem, 'content'> = {
+    borderColor: '#e5e5e5',
+    titleColor: '#353535',
+    descColor: '#b2b2b2',
+    bgColor: '#ffffff',
+    jumpMode: 'btn_jump',
+    imageUrl: '',
+    title: '',
+    desc: '',
+    subElemType: 'GH'
+}
+
+export const imageTextGh: ImageText = {
+    elementType: 'IMAGE_TEXT',
+    paddingTop: 40,
+    paddingBottom: 40,
+    alignMode: 0,
+    imageTextItem: [
+        {
+            ...imageTextItem,
+            content: {
+                ...ghITItemContent
+            }
+        }
+    ]
 }

+ 185 - 415
src/pages/launchSystemNew/components/addLandingPage/index.tsx

@@ -1,4 +1,4 @@
-import { Button, Checkbox, Col, Drawer, Form, Input, InputNumber, message, Modal, Radio, RadioChangeEvent, Row, Select, Slider, Space, Spin, Switch, Tooltip } from 'antd'
+import { Button, Col, Drawer, Form, Input, InputNumber, message, Modal, Radio, Row, Select, Slider, Space, Spin, Switch, Tooltip } from 'antd'
 import React, { useCallback, useEffect, useMemo, useState } from "react"
 import modal from "antd/lib/modal"
 import style from './index.less'
@@ -23,8 +23,8 @@ import arrayMove from "array-move";
 import ColorPicker from '@/components/ColorPicker1'
 import TextArea from 'antd/lib/input/TextArea'
 import moment from 'moment'
-import { TopImg, TopVideo, Text, Img as ImgProps, GhButton, TopSlider, LinkButton, Shelfnew, WxAutoButton, Floatbutton } from '../../req'
-import { imgContent, txtContent, ghContent, topvideoNewContent, linkContent, topsliderContent, topimgContent, topvideoContent, shelfnewContent, shelfnewContent2, btModelJumpGh, btModelJumpLink, btModelJumpLink103, btModelJumpGh103, jumpLink104, jumpGh104, jumpLink103, jumpGh103, wxAutoContent, jumpWxAuto104, jumpWxAuto103, btModelJumpWxAuto, btModelJumpWxAuto103, floatbuttonContent, floatbuttonBtTypeWxAuto, floatbuttonBtTypeGh } from './content'
+import { TopImg, TopVideo, Text, Img as ImgProps, GhButton, TopSlider, Floatbutton, ImageTextItem, ITItemGhSpec } from '../../req'
+import { imgContent, txtContent, ghContent, topvideoNewContent, topsliderContent, topimgContent, wxAutoContent, floatbuttonContent, floatbuttonBtTypeWxAuto, floatbuttonBtTypeGh, imageTextGh, ghITItemContent, wxAutoITItemContent, imageTextItem } from './content'
 import { landingPageReducer, Content, Props } from './landingPageReducer'
 
 import {
@@ -42,7 +42,7 @@ import {
 } from './sortable'
 import { useModel } from 'umi'
 import SelectCloud from '../selectCloud'
-import { getTypeKey, txtLength } from '@/utils/utils'
+import { getTypeKey, replaceSpecialTxt, txtLength } from '@/utils/utils'
 
 const { Option } = Select;
 
@@ -62,21 +62,6 @@ function AddLandingPage(props: Props) {
     const [shareDesc, setShareDesc] = useState<string>('')  // 分享描述
     const [pageName, setPageName] = useState<string>('')  // 落地页名称
     const [sort, setSort] = useState<number>(0) // 排序
-    const [imgSize, setImgSize] = useState<{
-        aspectRatio?: string, //宽高比
-        minWidth?: string,   //最小宽度
-        maxWidth?: string,   //最大宽度
-        minHeight?: string,  //最小高度
-        maxHeight?: string,  //最大高度
-        minMediaSize?: number, // 最小媒体大小
-        maxMediaSize?: number, // 最大媒体大小
-        minVideoLength?: number, // 最小视频时长
-        maxVideoLength?: number, // 最大视频时长
-        minVideoBitRate?: number, // 最小视频比特率
-        maxVideoBitRate?: number, // 最大视频比特率
-        mediaType?: 'IMG' | 'VIDEO' | 'PAGE',  // 内容类型 1 图片 2 视频
-    }>({})   // 要选择的素材信息
-    const [isFootlock, setIsFootlock] = useState<boolean>(false)  // 是否横板视频
     const [scType, setCcType] = useState<1 | 2 | 3 | 4 | 5>(1) // 视频 单图片 多图片处理
     const [sliderImgContent, setSliderImgContent] = useState<{ url: string, width?: number, height?: number }[]>([])  // 保存回填数据
     const [imgTextButtonShow, setImgTextButtonShow] = useState<boolean>(false)
@@ -133,7 +118,6 @@ function AddLandingPage(props: Props) {
             // dispatch({ type: 'init', params: { elementType: 'empty' } })
         }
     }, [id])
-    console.log('componentItem------>', componentItem);
 
 
     const config = {
@@ -155,7 +139,6 @@ function AddLandingPage(props: Props) {
             selectData = componentItem?.find((item: { comptActive: boolean }) => item.comptActive)
         }
         if (selectData?.elementType === "TOP_IMAGE") {
-            setIsFootlock(() => false)
             if (selectData?.adLocation === 'sns') { // 朋友圈信息流
                 if (selectData?.outerStyle === 0) { // 常规广告
                     init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 800, height: 800 }]], maxSize: 300 * 1024 })
@@ -166,11 +149,9 @@ function AddLandingPage(props: Props) {
                 init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 800, height: 800 }], [{ relation: '=', width: 800, height: 450 }], [{ relation: '=', width: 800, height: 640 }], [{ relation: '=', width: 640, height: 800 }]], maxSize: 300 * 1024 })
             }
         } else if (selectData?.elementType === 'IMAGE') { // 内容图片
-            setIsFootlock(() => false)
             init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 750, height: null }, { relation: '<=', width: null, height: 1536 }]], maxSize: 300 * 1024 })
         } else if (selectData?.elementType === 'TOP_SLIDER') { // 轮播图
             init({ mediaType: 'IMG', num: selectData?.imageUrlList?.length || 3, cloudSize: [[{ relation: '=', width: 800, height: 800 }]], maxSize: 300 * 1024 })
-            setIsFootlock(() => false)
         } else if (selectData?.elementType === 'TOP_VIDEO') { // 视频
             if (selectData?.adLocation === 'sns') { // 朋友圈信息流
                 if (selectData?.outerStyle === 0) { // 常规广告
@@ -180,18 +161,14 @@ function AddLandingPage(props: Props) {
                 }
             } else { // 公众号及其它
                 init({ mediaType: 'VIDEO', cloudSize: [[{ relation: '=', width: 750, height: null }, { relation: '<=', width: null, height: 1536 }]], maxSize: 20 * 1024 * 1024 })
-                setIsFootlock(() => false)
             }
-        } else if (selectData?.elementType === 'shelfnew') {  // 图文复合组件 1个
-            if (selectData?.type === '104') {
-                setIsFootlock(() => false)
-                setImgSize(() => ({ minWidth: '360', maxWidth: '360', minHeight: '360', maxHeight: '360', minMediaSize: 0, maxMediaSize: 307200, mediaType: 'IMG' }))
-            } else if (selectData?.type === '103') {
-                setIsFootlock(() => false)
-                setImgSize(() => ({ minWidth: '480', maxWidth: '480', minHeight: '480', maxHeight: '480', minMediaSize: 0, maxMediaSize: 307200, mediaType: 'IMG' }))
+        } else if (selectData?.elementType === 'IMAGE_TEXT') {  // 图文复合组件 1个
+            if (selectData?.imageTextItem?.length === 1) {
+                init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 360, height: 360 }]], maxSize: 300 * 1024 })
+            } else if (selectData?.imageTextItem?.length === 2) {
+                init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 480, height: 480 }]], maxSize: 300 * 1024 })
             }
         } else if (selectData?.elementType === 'FLOAT_BUTTON') {
-            setIsFootlock(() => false)
             init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 96, height: 96 }]], maxSize: 300 * 1024 })
         }
     }, [content])
@@ -255,8 +232,8 @@ function AddLandingPage(props: Props) {
                 newCon.push({ ...ghContent, comptActive: true } as any)
                 // } else if (con === 'JumpLink') {
                 //     newCon.push({ ...linkContent, comptActive: true } as any)
-                // } else if (con === 'shelfnew') {
-                //     newCon.push({ ...shelfnewContent, comptActive: true } as any)
+            } else if (con === 'IMAGE_TEXT') {
+                newCon.push({ ...imageTextGh, comptActive: true } as any)
             } else if (con === 'ENTERPRISE_WX') {
                 newCon.push({ ...wxAutoContent, comptActive: true } as any)
             } else if (con === 'FLOAT_BUTTON') {
@@ -333,10 +310,10 @@ function AddLandingPage(props: Props) {
             //     newContent.splice(index, 0, { ...linkContent });
             //     dispatch({ type: 'setCon', params: { content: newContent } })
             //     break;
-            // case 'shelfnew': // 图文复合组件
-            //     newContent.splice(index, 0, { ...shelfnewContent });
-            //     dispatch({ type: 'setCon', params: { content: newContent } })
-            //     break;
+            case 'IMAGE_TEXT': // 图文复合组件
+                newContent.splice(index, 0, JSON.parse(JSON.stringify(imageTextGh)));
+                dispatch({ type: 'setCon', params: { content: newContent } })
+                break;
             case 'ENTERPRISE_WX': // 图文复合组件
                 newContent.splice(index, 0, { ...wxAutoContent });
                 dispatch({ type: 'setCon', params: { content: newContent } })
@@ -387,8 +364,8 @@ function AddLandingPage(props: Props) {
                         return <SortableItemWxAuto key={`item-${value.elementType}-${index}`} index={index} data={{ length: content?.length, index }} item={value} click={(e: any) => { installActive(e, index) }} del={(e: any) => { delType(e, index) }} handleBtn={handleBtn} />
                         // } else if (value?.elementType === 'link') {
                         //     return <SortableItemJumpLink key={`item-${value.elementType}-${index}`} index={index} data={{ length: content?.length, index }} item={value} click={(e: any) => { installActive(e, index) }} del={(e: any) => { delType(e, index) }} handleBtn={handleBtn} />
-                        // } else if (value?.elementType === 'shelfnew') {
-                        //     return <SortableItemImgText key={`item-${value.elementType}-${index}`} index={index} data={{ length: content?.length, index }} item={value} click={(e: any) => { installActive(e, index) }} del={(e: any) => { delType(e, index) }} handleBtn={handleBtn} />
+                    } else if (value?.elementType === 'IMAGE_TEXT') {
+                        return <SortableItemImgText key={`item-${value.elementType}-${index}`} index={index} data={{ length: content?.length, index }} item={value} click={(e: any) => { installActive(e, index) }} del={(e: any) => { delType(e, index) }} handleBtn={handleBtn} />
                     } else {
                         return null
                     }
@@ -399,7 +376,7 @@ function AddLandingPage(props: Props) {
         }
     }
     /** 设置start */
-    const setCon = useCallback((key: string, value: any, isTopImg?: boolean) => {
+    const setCon = useCallback((key: string, value: any) => {
         let newContent = JSON.parse(JSON.stringify(content))
         let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
         let oldContent = newContent
@@ -424,21 +401,17 @@ function AddLandingPage(props: Props) {
                     <div className={`compt componentType41 ${content[0]?.comptActive && 'comptActive'}`} onClick={(e) => { installActive(e, 0) }}>
                         <div className={'componentWrap'}>
                             <div className={'componentContent'}>
-                                {
-                                    imageUrl ? <img src={imageUrl} style={{ display: 'block', width: '100%', margin: 0 }} /> : <div className={'default'} style={{ width: 375, height: 300, margin: 0 }}>
-                                        <div className={'defaultIcon'} style={{ marginTop: 80 }}>
-                                            <Topimg />
-                                        </div>
+                                {imageUrl ? <img src={imageUrl} style={{ display: 'block', width: '100%', margin: 0 }} /> : <div className={'default'} style={{ width: 375, height: 300, margin: 0 }}>
+                                    <div className={'defaultIcon'} style={{ marginTop: 80 }}>
+                                        <Topimg />
                                     </div>
-                                }
+                                </div>}
                             </div>
                         </div>
 
-                        {
-                            !imageUrl && <div className={'comptUpload'} style={{ margin: 0 }}>
-                                <button style={{ marginTop: 150 }} className={'comptEditButton'} onClick={() => { clickUpdateImg(1) }}>上传图片</button>
-                            </div>
-                        }
+                        {!imageUrl && <div className={'comptUpload'} style={{ margin: 0 }}>
+                            <button style={{ marginTop: 150 }} className={'comptEditButton'} onClick={() => { clickUpdateImg(1) }}>上传图片</button>
+                        </div>}
                         <section className={'comptEditBtns'}>
                             <div className={'comptEditBtnsInner'}>
                                 {imageUrl && <button onClick={() => { editSelectImg(imageUrl) }}><EditSvg /></button>}
@@ -640,152 +613,25 @@ function AddLandingPage(props: Props) {
     }, [content])
 
     /** 图文单个设置 */
-    const onShelfnewTxtCon = useCallback((value: string, count: number, parameter: string) => {
+    const onShelfnewTxtCon = useCallback((value: string, parameter: string) => {
         let newContent = JSON.parse(JSON.stringify(content))
         let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
         let oldContent = newContent
         if (selectIndex !== -1) {
             let selectCon = oldContent[selectIndex]
-            if (selectCon?.type === '103') {
-                let componentItem = selectCon?.layoutItems.componentItem[goodsCount]
-                let shelfnewItem = componentItem?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                shelfnewItem[count + 1][parameter] = value
-                if (count + 1 === 2 && parameter === 'content') {
-                    if (shelfnewItem[count + 1]?.name === '价格') {
-                        shelfnewItem[count + 1][parameter] = '¥' + value
-                    } else {
-                        shelfnewItem[count + 1][parameter] = value
-                    }
-                }
-            } else if (selectCon?.type === '104') {
-                let componentItem = selectCon?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                let textData = componentItem[1]?.layoutItems?.componentItem
-                textData[count][parameter] = value
-                if (count === 1 && parameter === 'content') {
-                    let name = textData[count]?.name
-                    if (name === '价格') {
-                        textData[count][parameter] = '¥' + value
-                    } else {
-                        textData[count][parameter] = value
-                    }
-                }
-            }
-            oldContent[selectIndex] = { ...selectCon }
-            dispatch({ type: 'setCon', params: { content: [...oldContent] } })
-        }
-    }, [content, goodsCount])
-    /** 设置描述类型 */
-    const onSetShelfnewDescType = useCallback((e: RadioChangeEvent) => {
-        let newContent = JSON.parse(JSON.stringify(content))
-        let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
-        let oldContent = newContent
-        if (selectIndex !== -1) {
-            let selectCon = oldContent[selectIndex]
-            if (selectCon?.type === '103') {
-                let componentItem = selectCon?.layoutItems.componentItem[goodsCount]
-                let shelfnewItem = componentItem?.layoutItems?.componentItem[0]
-                shelfnewItem.descType = e.target.value
-                let textData = shelfnewItem?.layoutItems?.componentItem
-                if (e.target.value === 'price') {
-                    textData[2] = {
-                        ...textData[2],
-                        name: '价格',
-                        content: '',
-                        fontColor: '#353535',
-                        fontSize: '32',
-                        showType: "1",
-                    }
-                } else {
-                    let { showType, ...desc } = textData[2]
-                    textData[2] = {
-                        ...desc,
-                        name: '描述',
-                        content: '',
-                        fontColor: '#4D4D4D',
-                        fontSize: '24'
-                    }
-                }
-            } else if (selectCon?.type === '104') {
-                let componentItem = selectCon?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                componentItem[1].descType = e.target.value
-                let textData = componentItem[1]?.layoutItems?.componentItem
-                if (e.target.value === 'price') {
-                    textData[1] = {
-                        ...textData[1],
-                        name: '价格',
-                        content: '',
-                        fontColor: '#353535',
-                        fontSize: '32',
-                        showType: "1",
-                    }
-                } else {
-                    let { showType, ...desc } = textData[1]
-                    textData[1] = {
-                        ...desc,
-                        name: '描述',
-                        content: '',
-                        fontColor: '#4D4D4D',
-                        fontSize: '24'
-                    }
-                }
+            let length = selectCon?.imageTextItem?.length || 0;
+            if (length === 2) {
+                let imageTextItem = selectCon?.imageTextItem[goodsCount]
+                imageTextItem[parameter] = value
+            } else if (length === 1) {
+                let imageTextItem = selectCon?.imageTextItem[0]
+                imageTextItem[parameter] = value
             }
             oldContent[selectIndex] = { ...selectCon }
             dispatch({ type: 'setCon', params: { content: [...oldContent] } })
         }
     }, [content, goodsCount])
 
-    /** 设置跳转方式 */
-    const onSetShelfnewJumpMode = useCallback((e: RadioChangeEvent) => {
-        let newContent = JSON.parse(JSON.stringify(content))
-        let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
-        let oldContent = newContent
-        if (selectIndex !== -1) {
-            let selectCon = oldContent[selectIndex]
-            if (selectCon?.type === '103') {
-                selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].jumpMode = e.target.value
-                let jumpData = selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3]
-                let widgetTypeV2 = jumpData?.widgetTypeV2
-                let componentItem = selectCon?.layoutItems?.componentItem[goodsCount]?.layoutItems?.componentItem
-                if (e.target.value === 'btn_jump') {
-                    selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem = [{ ...componentItem[0] }]
-                } else {
-                    switch (widgetTypeV2) {
-                        case 'link':
-                            componentItem[1] = { ...btModelJumpLink103, origBtnJumpUrl: jumpData?.origBtnJumpUrl }
-                            break
-                        case 'gh':
-                            componentItem[1] = { ...btModelJumpGh103, subType: jumpData?.subType, btnFontType: jumpData?.btnFontType }
-                            break
-                        case 'enterprise_wx_auto':
-                            componentItem[1] = { ...btModelJumpWxAuto103, subType: jumpData?.subType, btnFontType: jumpData?.btnFontType }
-                            break
-                    }
-                }
-            } else if (selectCon?.type === '104') {
-                selectCon.layoutItems.componentItem[0].jumpMode = e.target.value
-                let textData = selectCon?.layoutItems?.componentItem[0]?.layoutItems?.componentItem[1]?.layoutItems?.componentItem
-                let widgetTypeV2 = textData[2].widgetTypeV2
-                let componentItem = selectCon?.layoutItems?.componentItem
-                if (e.target.value === 'btn_jump') {
-                    selectCon.layoutItems.componentItem = [{ ...componentItem[0] }]
-                } else {
-                    switch (widgetTypeV2) {
-                        case 'link':
-                            componentItem[1] = { ...btModelJumpLink, origBtnJumpUrl: textData[2]?.origBtnJumpUrl }
-                            break
-                        case 'gh':
-                            componentItem[1] = { ...btModelJumpGh, subType: textData[2]?.subType, btnFontType: textData[2]?.btnFontType }
-                            break
-                        case 'enterprise_wx_auto':
-                            componentItem[1] = { ...btModelJumpWxAuto, subType: textData[2]?.subType, btnFontType: textData[2]?.btnFontType }
-                            break
-                    }
-                }
-            }
-            oldContent[selectIndex] = { ...selectCon }
-            dispatch({ type: 'setCon', params: { content: [...oldContent] } })
-        }
-    }, [content, goodsCount])
     // 设置图文跳转链接按钮
     const onSetShelfnewButton = useCallback((value: string) => {
         let newContent = JSON.parse(JSON.stringify(content))
@@ -793,86 +639,49 @@ function AddLandingPage(props: Props) {
         let oldContent = newContent
         if (selectIndex !== -1) {
             let selectCon = oldContent[selectIndex]
-            // "btn_jump" "total_jump"
-            if (selectCon?.type === '103') {
-                let jumpMode = selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].jumpMode
-                let btnContent = selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3]
-                if (value === 'link') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[1] = { ...btModelJumpLink103 }
-                    }
-                    if (btnContent?.horizontalAlignment) {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpLink103, horizontalAlignment: '1' }
-                    } else {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpLink103 }
-                    }
-                } else if (value === 'gh') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[1] = { ...btModelJumpGh103 }
-                    }
-                    if (btnContent?.horizontalAlignment) {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpGh103, horizontalAlignment: '1' }
-                    } else {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpGh103 }
-                    }
-                } else if (value === 'enterprise_wx_auto') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[1] = { ...btModelJumpWxAuto103 }
-                    }
-                    if (btnContent?.horizontalAlignment) {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpWxAuto103, horizontalAlignment: '1' }
-                    } else {
-                        selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3] = { ...jumpWxAuto103 }
-                    }
+            let length = selectCon?.imageTextItem?.length || 0;
+            if (length === 2) {
+                let imageTextItem = selectCon?.imageTextItem[goodsCount]
+                imageTextItem.subElemType = value
+                switch(value) {
+                    case 'GH':
+                        imageTextItem.content = JSON.parse(JSON.stringify(ghITItemContent))
+                        break;
+                    case 'ENTERPRISE_WX':
+                        imageTextItem.content = JSON.parse(JSON.stringify(wxAutoITItemContent))
+                        break
                 }
-            } else if (selectCon?.type === '104') {
-                let jumpMode = selectCon.layoutItems.componentItem[0].jumpMode
-                if (value === 'link') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[1] = { ...btModelJumpLink }
-                    }
-                    selectCon.layoutItems.componentItem[0].layoutItems.componentItem[1].layoutItems.componentItem[2] = { ...jumpLink104 }
-                } else if (value === 'gh') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[1] = { ...btModelJumpGh }
-                    }
-                    selectCon.layoutItems.componentItem[0].layoutItems.componentItem[1].layoutItems.componentItem[2] = { ...jumpGh104 }
-                } else if (value === 'enterprise_wx_auto') {
-                    if (jumpMode === "total_jump") {
-                        selectCon.layoutItems.componentItem[1] = { ...btModelJumpWxAuto }
-                    }
-                    selectCon.layoutItems.componentItem[0].layoutItems.componentItem[1].layoutItems.componentItem[2] = { ...jumpWxAuto104 }
+            } else if (length === 1) {
+                let imageTextItem = selectCon?.imageTextItem[0]
+                imageTextItem.subElemType = value
+                switch(value) {
+                    case 'GH':
+                        imageTextItem.content = JSON.parse(JSON.stringify(ghITItemContent))
+                        break;
+                    case 'ENTERPRISE_WX':
+                        imageTextItem.content = JSON.parse(JSON.stringify(wxAutoITItemContent))
+                        break
                 }
             }
             oldContent[selectIndex] = { ...selectCon }
             dispatch({ type: 'setCon', params: { content: [...oldContent] } })
         }
     }, [content, goodsCount])
+
     // 配置图文跳转链接按钮字段
-    const onSetShelfnewButtonField = useCallback((field: string, value: string) => {
+    const onSetShelfnewButtonField = useCallback((field: string, value: string | number) => {
         let newContent = JSON.parse(JSON.stringify(content))
         let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
         let oldContent = newContent
         if (selectIndex !== -1) {
             let selectCon = oldContent[selectIndex]
-            if (selectCon?.type === '103') {
-                let jumpMode = selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].jumpMode
-                selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3][field] = value
-                if (field === 'btnBorderColorTheme') {
-                    selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[0].layoutItems.componentItem[3]['borderSize'] = "2"
-                }
-                if (jumpMode === 'total_jump' && (field === 'subType' || field === 'btnFontType')) {
-                    selectCon.layoutItems.componentItem[goodsCount].layoutItems.componentItem[1][field] = value
-                }
-            } else if (selectCon?.type === '104') {
-                let jumpMode = selectCon.layoutItems.componentItem[0].jumpMode
-                selectCon.layoutItems.componentItem[0].layoutItems.componentItem[1].layoutItems.componentItem[2][field] = value
-                if (field === 'btnBorderColorTheme') {
-                    selectCon.layoutItems.componentItem[0].layoutItems.componentItem[1].layoutItems.componentItem[2]['borderSize'] = "2"
-                }
-                if (jumpMode === 'total_jump' && (field === 'subType' || field === 'btnFontType')) {
-                    selectCon.layoutItems.componentItem[1][field] = value
-                }
+            let length = selectCon.imageTextItem.length
+            if (length === 2) {
+                let content = selectCon.imageTextItem[goodsCount].content
+                content[field] = value
+            } else if (length === 1) {
+                let content = selectCon.imageTextItem[0].content
+                content[field] = value
             }
             oldContent[selectIndex] = { ...selectCon }
             dispatch({ type: 'setCon', params: { content: [...oldContent] } })
@@ -884,53 +693,27 @@ function AddLandingPage(props: Props) {
         let newContent = JSON.parse(JSON.stringify(content))
         let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
         if (selectIndex !== -1) {
-            if (value === '103') {
-                newContent[selectIndex] = { ...shelfnewContent2, comptActive: true }
-            } else if (value === '104') {
-                newContent[selectIndex] = { ...shelfnewContent, comptActive: true }
-            }
-            dispatch({ type: 'setCon', params: { content: [...newContent] } })
-        }
-    }, [content])
-
-    // 设置对齐方式
-    const setAilgin103 = useCallback((align: number) => {
-        let newContent = JSON.parse(JSON.stringify(content))
-        let selectIndex = newContent?.findIndex((item: Content) => item.comptActive)
-        if (selectIndex !== -1) {
-            let selectCon = newContent[selectIndex]
-            selectCon.wxad_align = align
-            let componentItem = selectCon?.layoutItems?.componentItem?.map((item: any) => {
-                let shelfnewItem = item?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                if (align === 0) {
-                    shelfnewItem = shelfnewItem?.map((oldItem: any, index: number) => {
-                        if (index === 0) {
-                            return oldItem
-                        } else if (index === 1 || index === 2) {
-                            let { textAlignment, ...newItem } = oldItem
-                            return newItem
-                        } else {
-                            let { horizontalAlignment, ...newItem } = oldItem
-                            return newItem
+            if (value === '2') {
+                newContent[selectIndex].imageTextItem = [
+                    JSON.parse(JSON.stringify({
+                        ...imageTextItem,
+                        content: {
+                            ...ghITItemContent
                         }
-                    })
-                } else {
-                    shelfnewItem = shelfnewItem?.map((newItem: any, index: number) => {
-                        if (index === 0) {
-                            return newItem
-                        } else if (index === 1 || index === 2) {
-                            return { ...newItem, textAlignment: "1" }
-                        } else {
-                            return { ...newItem, horizontalAlignment: "1" }
+                    })),
+                    JSON.parse(JSON.stringify({
+                        ...imageTextItem,
+                        content: {
+                            ...ghITItemContent
                         }
-
-                    })
-                }
-                item.layoutItems.componentItem[0].layoutItems.componentItem = shelfnewItem
-                return item
-            })
-            selectCon.layoutItems.componentItem = componentItem
-            newContent[selectIndex] = { ...selectCon }
+                    }))
+                ]
+            } else if (value === '1') {
+                newContent[selectIndex].alignMode = 0
+                let imageTextItem = newContent[selectIndex].imageTextItem
+                let newImageTextItem = imageTextItem.splice(0, 1)
+                newContent[selectIndex].imageTextItem = newImageTextItem
+            }
             dispatch({ type: 'setCon', params: { content: [...newContent] } })
         }
     }, [content])
@@ -982,45 +765,39 @@ function AddLandingPage(props: Props) {
         if (selectCon) {
             let { elementType, adLocation, outerStyle, imageUrlList, imageUrl, text, fontColor, fontSize, fontStyle, textAlignment,
                 paddingTop, paddingBottom, fastFollow, btnTitle, btnFontType, btnBorderColorTheme, btnBgColorTheme, useIcon,
-                styleType, type, initHeight, outerUseTopMaterial, componentCount, activeIndex, componentGroupList, origBtnJumpUrl,
-                layoutItems, borderColor, bgColor, title, desc, titleColor, descColor, componentItem, appearType, disappearType } = selectCon
-            let descType = "text"
-            let jumpMode = "btn_jump"
-            let shelfnewImgData: { pureImageUrl: string } = { pureImageUrl: '' }  // 图片信息
-            let shelfnewTitleData: { content: string, fontColor: string } = { content: "", fontColor: "#353535" } // 标题信息
-            let shelfnewDescData: { name: string, content: string, fontColor: string } = { name: '', content: "", fontColor: "#B2B2B2" } // 描述信息
-            let shelfnewBtData: {
-                widgetTypeV2: string, btnTitle: string, btnBgColorTheme: string,
-                subType: '17' | '1', btnFontType: '0' | '1',
-                btnBorderColorTheme: string, fontColor: string, origBtnJumpUrl: string,
-                custorData: any[]
-            } = { widgetTypeV2: '', btnFontType: '0', subType: '17', btnTitle: "", btnBgColorTheme: "#07C160", btnBorderColorTheme: "#FFFFFF", fontColor: "#FFFFFF", origBtnJumpUrl: "", custorData: [] }
-
+                styleType, imageTextItem, initHeight, alignMode, activeIndex, title, desc, titleColor, descColor, componentItem, appearType, disappearType } = selectCon
+            let imgTextData: ImageTextItem = {
+                borderColor: '#e5e5e5',
+                titleColor: '#353535',
+                descColor: '#b2b2b2',
+                bgColor: '#ffffff',
+                jumpMode: 'btn_jump',
+                imageUrl: '',
+                title: '',
+                desc: '',
+                subElemType: 'GH',
+                content: {
+                    btnTitle: '关注公众号',
+                    fontColor: '#FFFFFF',
+                    btnBgColorTheme: '#07C160',
+                    btnBorderColorTheme: '#FFFFFF',
+                    btnFontType: 0,
+                    useIcon: 0,
+                    fastFollow: 1,
+                    paddingTop: 28,
+                    paddingBottom: 28,
+                }
+            }
+            let imageTextItemIndex = 1;
             let custorGroup = []
             if (elementType === 'enterprise_wx_auto') {
                 custorGroup = selectCon?.custorData || []
-            } else if (elementType === 'shelfnew') {
-                if (type === '104') {  // 图文复合组件 单个
-                    let shelfnewData = layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                    jumpMode = layoutItems?.componentItem[0]?.jumpMode
-                    descType = shelfnewData[1]?.descType
-                    shelfnewImgData = shelfnewData[0]
-                    shelfnewTitleData = shelfnewData[1]?.layoutItems?.componentItem[0]
-                    shelfnewDescData = shelfnewData[1]?.layoutItems?.componentItem[1]
-                    shelfnewBtData = shelfnewData[1]?.layoutItems?.componentItem[2]
-                } else if (type === '103') {
-                    // goodsCount
-                    let componentItems = layoutItems.componentItem
-                    let componentItem = componentItems[goodsCount]
-                    borderColor = componentItem?.borderColor || "#e5e5e5"
-                    bgColor = componentItem?.bgColor || "#ffffff"
-                    jumpMode = componentItem?.layoutItems?.componentItem[0]?.jumpMode
-                    let shelfnewItem = componentItem?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                    descType = componentItem?.layoutItems?.componentItem[0]?.descType
-                    shelfnewImgData = shelfnewItem[0]
-                    shelfnewTitleData = shelfnewItem[1]
-                    shelfnewDescData = shelfnewItem[2]
-                    shelfnewBtData = shelfnewItem[3]
+            } else if (elementType === 'IMAGE_TEXT') {
+                imageTextItemIndex = imageTextItem?.length || 1
+                if (imageTextItemIndex === 1) {  // 图文复合组件 单个
+                    imgTextData = imageTextItem[0]
+                } else if (imageTextItemIndex === 2) {
+                    imgTextData = imageTextItem[goodsCount]
                 }
             }
 
@@ -1035,7 +812,7 @@ function AddLandingPage(props: Props) {
                             <div className="adui-form-item">
                                 <div className="adui-form-label">广告位</div>
                                 <div className="adui-form-control">
-                                    <Radio.Group onChange={(e) => { setCon('adLocation', e.target.value, true) }} value={adLocation} size='small'>
+                                    <Radio.Group onChange={(e) => { setCon('adLocation', e.target.value) }} value={adLocation} size='small'>
                                         <Radio value='sns'>朋友圈信息流</Radio>
                                         <Radio value='gzh'>公众号及其他</Radio>
                                     </Radio.Group>
@@ -1044,7 +821,7 @@ function AddLandingPage(props: Props) {
                             {adLocation === 'sns' && <div className="adui-form-item">
                                 <div className="adui-form-label">外层样式</div>
                                 <div className="adui-form-control">
-                                    <Select value={outerStyle} style={{ width: 100 }} onChange={(e) => { setCon('outerStyle', e, true) }}>
+                                    <Select value={outerStyle} style={{ width: 100 }} onChange={(e) => { setCon('outerStyle', e) }}>
                                         <Option value={0}>常规广告</Option>
                                         <Option value={1}>卡片广告</Option>
                                     </Select>
@@ -1187,10 +964,10 @@ function AddLandingPage(props: Props) {
                                     <div className="form section">
                                         <div className="form-caption">推广文案</div>
                                         <TextArea
-                                            placeholder="请输入"
+                                            placeholder={`请输入(不包含<>&'"/\以及TAB、换行、回车键)`}
                                             autoSize={{ minRows: 4, maxRows: 6 }}
                                             value={text}
-                                            onChange={(e) => { setCon('text', e.target.value) }}
+                                            onChange={(e) => { setCon('text', replaceSpecialTxt(e.target.value)) }}
                                         />
                                     </div>
                                     <div className="form section">
@@ -1518,40 +1295,22 @@ function AddLandingPage(props: Props) {
                                                     </div> */}
                                                 </div>
                                                     :
-                                                    elementType === 'shelfnew' ? <div className="widget">
-                                                        {/* {imgTextButtonShow ? <>
+                                                    elementType === 'IMAGE_TEXT' ? <div className="widget">
+                                                        {imgTextButtonShow ? <>
                                                             <div className="caption section goBack" onClick={() => { setImgTextButtonShow(false) }}>
                                                                 <SwapLeftOutlined />
                                                                 <span>返回</span>
                                                             </div>
-                                                            {shelfnewBtData.widgetTypeV2 === 'gh' || shelfnewBtData.widgetTypeV2 === 'link' || shelfnewBtData.widgetTypeV2 === 'enterprise_wx_auto' ? <>
+                                                            {imgTextData.subElemType === 'GH' || imgTextData.subElemType === 'ENTERPRISE_WX' ? <>
 
-                                                                {shelfnewBtData.widgetTypeV2 === 'link' ? <div className="form section">
-                                                                    <div className="form-caption">链接设置</div>
-                                                                    <Input placeholder="以 http:// 或 https:// 开头" value={shelfnewBtData?.origBtnJumpUrl} onChange={(e) => { onSetShelfnewButtonField('origBtnJumpUrl', e.target.value) }} />
-                                                                </div> : shelfnewBtData.widgetTypeV2 === 'gh' ? <div className="form section">
+                                                                {imgTextData.subElemType === 'GH' && <div className="form section">
                                                                     <Space>
-                                                                        <Switch size="small" checked={shelfnewBtData?.subType === '17' ? true : false} onChange={(e) => { onSetShelfnewButtonField('subType', e ? '17' : '1') }} />
+                                                                        <Switch size="small" checked={(imgTextData?.content as ITItemGhSpec)?.fastFollow === 1 ? true : false} onChange={(e) => { onSetShelfnewButtonField('fastFollow', e ? 1 : 0) }} />
                                                                         一键关注
                                                                         <Tooltip placement="top" title={'唤起公众号简介的半屏面板,点击其中按钮直接关注公众号'}>
                                                                             <QuestionCircleOutlined />
                                                                         </Tooltip>
                                                                     </Space>
-                                                                </div> : <div className="form section">
-                                                                    <div className="form-caption">客服设置</div>
-                                                                    <div className="adui-form-item">
-                                                                        <div className="adui-form-label">客服分配</div>
-                                                                        <div className="adui-form-control">
-                                                                            <div className="fl-sb">
-                                                                                <Button size="small" onClick={clickCustorGroup}>{shelfnewBtData?.custorData?.length > 0 ? '修改客服组' : '选择客服组'}</Button>
-                                                                            </div>
-                                                                        </div>
-                                                                    </div>
-                                                                    {shelfnewBtData?.custorData?.length > 0 && <div className='adui-form-item custorGroup'>
-                                                                        {shelfnewBtData?.custorData?.map((item: { label: string, custorData: any[], id: number }) => <div key={item.id}>
-                                                                            <strong>{item.label}:</strong><span>{item.custorData[0]?.name}</span>
-                                                                        </div>)}
-                                                                    </div>}
                                                                 </div>}
 
                                                                 <div className="form section">
@@ -1562,12 +1321,12 @@ function AddLandingPage(props: Props) {
                                                                             <div className="fl-sb">
                                                                                 <div style={{ display: 'flex', justifyContent: 'flex-start', alignItems: 'center' }}>
                                                                                     <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', border: '1px solid #d9d9d9', borderRadius: 2, paddingRight: 8 }}>
-                                                                                        <Input maxLength={5} style={{ width: 90 }} bordered={false} value={shelfnewBtData?.btnTitle} onChange={(e) => { onSetShelfnewButtonField('btnTitle', e.target.value) }} /> <span>{shelfnewBtData?.btnTitle?.length}/5</span>
+                                                                                        <Input maxLength={5} style={{ width: 90 }} bordered={false} value={imgTextData?.content?.btnTitle} onChange={(e) => { onSetShelfnewButtonField('btnTitle', e.target.value) }} /> <span>{imgTextData?.content?.btnTitle?.length}/5</span>
                                                                                     </div>
                                                                                 </div>
-                                                                                <Radio.Group onChange={(e) => { onSetShelfnewButtonField('btnFontType', e.target.value) }} value={shelfnewBtData?.btnFontType}>
-                                                                                    <Radio.Button value="0">常规</Radio.Button>
-                                                                                    <Radio.Button value="1">加粗</Radio.Button>
+                                                                                <Radio.Group onChange={(e) => { onSetShelfnewButtonField('btnFontType', e.target.value) }} value={imgTextData?.content?.btnFontType}>
+                                                                                    <Radio.Button value={0}>常规</Radio.Button>
+                                                                                    <Radio.Button value={1}>加粗</Radio.Button>
                                                                                 </Radio.Group>
                                                                             </div>
                                                                         </div>
@@ -1575,19 +1334,19 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-item" style={{ alignItems: 'center' }}>
                                                                         <div className="adui-form-label">字体色</div>
                                                                         <div className="adui-form-control">
-                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('fontColor', color) }} color={shelfnewBtData?.fontColor}></ColorPicker><div className="colorShow">{shelfnewBtData?.fontColor}</div></Space>
+                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('fontColor', color) }} color={imgTextData?.content?.fontColor}></ColorPicker><div className="colorShow">{imgTextData?.content?.fontColor}</div></Space>
                                                                         </div>
                                                                     </div>
                                                                     <div className="adui-form-item" style={{ alignItems: 'center' }}>
                                                                         <div className="adui-form-label">边框色</div>
                                                                         <div className="adui-form-control">
-                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('btnBorderColorTheme', color) }} color={shelfnewBtData?.btnBorderColorTheme}></ColorPicker><div className="colorShow">{shelfnewBtData?.btnBorderColorTheme}</div></Space>
+                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('btnBorderColorTheme', color) }} color={imgTextData?.content?.btnBorderColorTheme}></ColorPicker><div className="colorShow">{imgTextData?.content?.btnBorderColorTheme}</div></Space>
                                                                         </div>
                                                                     </div>
                                                                     <div className="adui-form-item" style={{ alignItems: 'center' }}>
                                                                         <div className="adui-form-label">背景色</div>
                                                                         <div className="adui-form-control">
-                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('btnBgColorTheme', color) }} color={shelfnewBtData?.btnBgColorTheme}></ColorPicker><div className="colorShow">{shelfnewBtData?.btnBgColorTheme}</div></Space>
+                                                                            <Space><ColorPicker onColor={(color: string) => { onSetShelfnewButtonField('btnBgColorTheme', color) }} color={imgTextData?.content?.btnBgColorTheme}></ColorPicker><div className="colorShow">{imgTextData?.content?.btnBgColorTheme}</div></Space>
                                                                         </div>
                                                                     </div>
                                                                 </div>
@@ -1601,11 +1360,11 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-label">类型</div>
                                                                     <div className="adui-form-control">
                                                                         <>
-                                                                            <Radio.Group onChange={(e) => { setShelfnewType(e.target.value) }} value={type}>
-                                                                                <Radio value='104'>一行1个</Radio>
-                                                                                <Radio value='103'>一行2个</Radio>
+                                                                            <Radio.Group onChange={(e) => { setShelfnewType(e.target.value) }} value={imageTextItemIndex.toString()}>
+                                                                                <Radio value='1'>一行1个</Radio>
+                                                                                <Radio value='2'>一行2个</Radio>
                                                                             </Radio.Group>
-                                                                            {type === '103' && <Radio.Group onChange={(e) => { setGoodsCount(e.target.value) }} value={goodsCount} size='small' style={{ marginTop: 10 }}>
+                                                                            {imageTextItemIndex === 2 && <Radio.Group onChange={(e) => { setGoodsCount(e.target.value) }} value={goodsCount} size='small' style={{ marginTop: 10 }}>
                                                                                 <Radio.Button value={0}>商品1</Radio.Button>
                                                                                 <Radio.Button value={1}>商品2</Radio.Button>
                                                                             </Radio.Group>}
@@ -1619,8 +1378,8 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-label">配图</div>
                                                                     <div className="adui-form-control">
                                                                         <div>
-                                                                            <Button onClick={() => { setCcType(4); setSelectImgVisible(true); setNum(1) }}>上传图片</Button>
-                                                                            <div style={{ marginTop: 4, fontSize: 12, color: '#636363' }}>{type === '103' ? '尺寸:480像素*480像素' : '尺寸:360像素*360像素'}</div>
+                                                                            <Button onClick={() => { setCcType(4); setSelectImgVisible(true); }}>上传图片</Button>
+                                                                            <div style={{ marginTop: 4, fontSize: 12, color: '#636363' }}>{imageTextItemIndex === 2 ? '尺寸:480像素*480像素' : '尺寸:360像素*360像素'}</div>
                                                                             <div style={{ marginTop: 4, fontSize: 12, color: '#636363' }}>格式:不超过300k</div>
                                                                         </div>
                                                                     </div>
@@ -1628,33 +1387,27 @@ function AddLandingPage(props: Props) {
                                                                 <div className="adui-form-item" style={{ alignItems: 'flex-start' }}>
                                                                     <div className="adui-form-label" style={{ marginTop: 6 }}>标题</div>
                                                                     <div className="adui-form-control">
-                                                                        <Input.TextArea placeholder="请输入标题" onChange={(e) => { onShelfnewTxtCon(e.target.value?.replace(/\r|\n/ig, ""), 0, 'content') }} value={shelfnewTitleData?.content} showCount maxLength={type === '104' ? 12 : 8} autoSize />
+                                                                        <Input.TextArea placeholder="请输入标题" onChange={(e) => { onShelfnewTxtCon(e.target.value?.replace(/\r|\n/ig, ""), 'title') }} value={imgTextData?.title} showCount maxLength={imageTextItemIndex === 1 ? 8 : 12} autoSize />
                                                                     </div>
                                                                 </div>
 
-                                                                <div className="adui-form-item">
+                                                                <div className="adui-form-item" style={{ alignItems: 'flex-start' }}>
                                                                     <div className="adui-form-label">描述</div>
                                                                     <div className="adui-form-control">
                                                                         <Space direction="vertical" style={{ width: '100%' }}>
-                                                                            <Radio.Group onChange={onSetShelfnewDescType} value={descType}>
+                                                                            <Radio.Group defaultValue='text'>
                                                                                 <Radio value="text">文字</Radio>
-                                                                                <Radio value="price">价格</Radio>
+                                                                                <Radio value="price" disabled>价格</Radio>
                                                                             </Radio.Group>
-                                                                            {descType === 'text' ?
-                                                                                <Input.TextArea placeholder="请输入描述" onChange={(e) => { onShelfnewTxtCon(e.target.value?.replace(/\r|\n/ig, ""), 1, 'content') }} value={shelfnewDescData?.content} showCount maxLength={type === '104' ? 15 : 10} autoSize={{ minRows: 2, maxRows: 2 }} /> :
-                                                                                <div>
-                                                                                    <Input placeholder="请输入" suffix="元" onChange={(e) => { onShelfnewTxtCon(e.target.value, 1, 'content') }} style={{ width: 150 }} value={shelfnewDescData?.content?.split('¥')[1]} />
-                                                                                    <div style={{ fontSize: 12, color: '#636363', marginTop: 5 }}>价格范围0.01~999,999,999.99元</div>
-                                                                                </div>
-                                                                            }
+                                                                            <Input.TextArea placeholder="请输入描述" onChange={(e) => { onShelfnewTxtCon(e.target.value?.replace(/\r|\n/ig, ""), 'desc') }} value={imgTextData?.desc} showCount maxLength={imageTextItemIndex === 1 ? 10 : 15} autoSize={{ minRows: 2, maxRows: 2 }} />
                                                                         </Space>
                                                                     </div>
                                                                 </div>
 
-                                                                {type === '103' && <div className="adui-form-item">
+                                                                {imageTextItemIndex === 2 && <div className="adui-form-item">
                                                                     <div className="adui-form-label">对齐</div>
                                                                     <div className="adui-form-control">
-                                                                        <Radio.Group onChange={(e) => { setAilgin103(e.target.value) }} value={wxad_align}>
+                                                                        <Radio.Group onChange={(e) => { setCon('alignMode', e.target.value) }} value={alignMode}>
                                                                             <Radio value={0}>左对齐</Radio>
                                                                             <Radio value={1}>居中对齐</Radio>
                                                                         </Radio.Group>
@@ -1666,12 +1419,12 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-control">
                                                                         <Space style={{ width: '100%' }} size="large" className="shelfnewColor">
                                                                             <div>
-                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 0, 'fontColor') }} color={shelfnewTitleData?.fontColor}></ColorPicker> <div className="colorShow">{shelfnewTitleData?.fontColor}</div></Space>
+                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 'titleColor') }} color={imgTextData.titleColor}></ColorPicker> <div className="colorShow">{imgTextData.titleColor}</div></Space>
                                                                                 <div className="colorName">标题</div>
                                                                             </div>
                                                                             <div>
-                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 1, 'fontColor') }} color={shelfnewDescData?.fontColor}></ColorPicker> <div className="colorShow">{shelfnewDescData?.fontColor}</div></Space>
-                                                                                <div className="colorName">{shelfnewDescData?.name}</div>
+                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 'descColor') }} color={imgTextData.descColor}></ColorPicker> <div className="colorShow">{imgTextData.descColor}</div></Space>
+                                                                                <div className="colorName">{/*shelfnewDescData?.name*/'描述'}</div>
                                                                             </div>
                                                                         </Space>
                                                                     </div>
@@ -1681,11 +1434,11 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-control">
                                                                         <Space style={{ width: '100%' }} size="large" className="shelfnewColor">
                                                                             <div>
-                                                                                <Space><ColorPicker onColor={(color: string) => { setCon('borderColor', color) }} color={borderColor}></ColorPicker> <div className="colorShow">{borderColor}</div></Space>
+                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 'borderColor') }} color={imgTextData.borderColor}></ColorPicker> <div className="colorShow">{imgTextData.borderColor}</div></Space>
                                                                                 <div className="colorName">边框</div>
                                                                             </div>
                                                                             <div>
-                                                                                <Space><ColorPicker onColor={(color: string) => { setCon('bgColor', color) }} color={bgColor}></ColorPicker> <div className="colorShow">{bgColor}</div></Space>
+                                                                                <Space><ColorPicker onColor={(color: string) => { onShelfnewTxtCon(color, 'bgColor') }} color={imgTextData.bgColor}></ColorPicker> <div className="colorShow">{imgTextData.bgColor}</div></Space>
                                                                                 <div className="colorName">背景</div>
                                                                             </div>
                                                                         </Space>
@@ -1720,7 +1473,7 @@ function AddLandingPage(props: Props) {
                                                                 <div className="adui-form-item">
                                                                     <div className="adui-form-label">跳转方式</div>
                                                                     <div className="adui-form-control">
-                                                                        <Radio.Group onChange={onSetShelfnewJumpMode} value={jumpMode}>
+                                                                        <Radio.Group onChange={(e) => onShelfnewTxtCon(e.target.value, 'jumpMode')} value={imgTextData.jumpMode}>
                                                                             <Radio value="btn_jump">按钮跳转</Radio>
                                                                             <Radio value="total_jump">全局跳转</Radio>
                                                                         </Radio.Group>
@@ -1730,17 +1483,17 @@ function AddLandingPage(props: Props) {
                                                                     <div className="adui-form-label">按钮类型</div>
                                                                     <div className="adui-form-control">
                                                                         <Space>
-                                                                            <Select style={{ width: 120 }} value={shelfnewBtData.widgetTypeV2} onChange={(e) => { onSetShelfnewButton(e) }}>
-                                                                                <Option value="link">跳转链接</Option>
-                                                                                <Option value="gh">关注公众号</Option>
-                                                                                <Option value="enterprise_wx_auto">添加商家微信</Option>
+                                                                            <Select style={{ width: 120 }} value={imgTextData.subElemType} onChange={(e) => { onSetShelfnewButton(e) }}>
+                                                                                {/* <Option value="link">跳转链接</Option> */}
+                                                                                <Option value="GH">关注公众号</Option>
+                                                                                <Option value="ENTERPRISE_WX">添加商家微信</Option>
                                                                             </Select>
                                                                             <Button type="link" size="small" onClick={() => { setImgTextButtonShow(true) }}>配置</Button>
                                                                         </Space>
                                                                     </div>
                                                                 </div>
                                                             </div>
-                                                        </>} */}
+                                                        </>}
                                                     </div>
                                                         :
                                                         elementType === 'FLOAT_BUTTON' ? <div style={{ height: '100%' }}>
@@ -1928,13 +1681,13 @@ function AddLandingPage(props: Props) {
         setSliderImgContent([])
         setCcType(1)
         setSelectImgVisible(true)
-    }, [imgSize, scType, content, sliderImgContent])
+    }, [scType, content, sliderImgContent])
 
     /** 选择视频 */
     const clickUpdateVideo = useCallback(() => {
         setCcType(2)
         setSelectImgVisible(true)
-    }, [imgSize, scType])
+    }, [scType])
 
     /** 弹窗返回设置图片 */
     const setImg = useCallback((value: any[]) => {
@@ -1974,18 +1727,15 @@ function AddLandingPage(props: Props) {
         } else if (scType === 4) { // 设置图文复合组件图片
             if (selectIndex !== -1) {
                 let selectCon = oldContent[selectIndex]
-                if (selectCon?.type === '104') {
-                    let componentItem = selectCon?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                    componentItem[0] = {
-                        ...componentItem[0],
-                        pureImageUrl: value[0]?.content
-                    }
+                let length = selectCon?.imageTextItem?.length || 0
+                if (length === 1) {
+                    let imageTextItem = selectCon?.imageTextItem[0]
+                    imageTextItem.imageUrl = value[0]?.url
                     oldContent[selectIndex] = { ...selectCon }
                     dispatch({ type: 'setCon', params: { content: [...oldContent] } })
-                } else if (selectCon?.type === '103') {
-                    let componentItem = selectCon?.layoutItems.componentItem[goodsCount]
-                    let shelfnewItem = componentItem?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                    shelfnewItem[0].pureImageUrl = value[0]?.content
+                } else if (length === 2) {
+                    let imageTextItem = selectCon?.imageTextItem[goodsCount]
+                    imageTextItem.imageUrl = value[0]?.url
                     oldContent[selectIndex] = { ...selectCon }
                     dispatch({ type: 'setCon', params: { content: [...oldContent] } })
                 }
@@ -2065,8 +1815,14 @@ function AddLandingPage(props: Props) {
                     message.error('客服组按钮按钮文案未填写')
                     return false
                 }
-            } else if (item?.elementType === "shelfnew") {
-                return true
+            } else if (item?.elementType === "IMAGE_TEXT") {
+                return item?.imageTextItem?.every((imageTextItem: ImageTextItem) => {
+                    if (imageTextItem?.imageUrl && imageTextItem?.title && imageTextItem?.desc) {
+                        return true
+                    }
+                    message.error('请完善图文复合组件内容')
+                    return false
+                })
             } else {
                 return true
             }
@@ -2133,7 +1889,21 @@ function AddLandingPage(props: Props) {
             let { elementType, comptActive, activeIndex, ...data } = item
             let typeKey = getTypeKey(elementType)
             let newItem = { elementType }
-            newItem[typeKey] = data
+            if (elementType === 'IMAGE_TEXT') {
+                let { imageTextItem, ...spec } = data as any
+                let newImageTextItem = imageTextItem?.map((item: any) => {
+                    let { content, ...newItem } = item
+                    let key = getTypeKey(newItem.subElemType)
+                    newItem[key] = content
+                    return newItem
+                })
+                newItem[typeKey] = {
+                    ...spec,
+                    imageTextItem: newImageTextItem
+                }
+            } else {
+                newItem[typeKey] = data
+            }
             return newItem
         })
 
@@ -2265,7 +2035,7 @@ function AddLandingPage(props: Props) {
                         </div>
                         <div className={style.title}>营销组件</div>
                         <div className={style.assembly}>
-                            {/* <div {...getDragPropsCon(`shelfnew`)}> <ImgText /> <span className="my">图文复合组件</span> </div> */}
+                            {/* <div {...getDragPropsCon(`IMAGE_TEXT`)}> <ImgText /> <span className="my">图文复合组件</span> </div> */}
                             {componentItem?.some((item: { elementType: string }) => item.elementType === 'FLOAT_BUTTON') ?
                                 <div className={style.disabled}> <FloatbuttonSvg /> <span>悬浮组件</span></div> : <div {...getDragPropsCon(`FLOAT_BUTTON`)}> <FloatbuttonSvg /> <span className="my">悬浮组件</span></div>
                             }

+ 1 - 1
src/pages/launchSystemNew/components/addLandingPage/index1.less

@@ -647,7 +647,7 @@
 
   .shelfItemContent {
     // flex: 1;
-    width: calc(100% - 90px);
+    // width: calc(100% - 90px);
     height: 100%;
     min-width: 0;
     text-align: left;

+ 2 - 2
src/pages/launchSystemNew/components/addLandingPage/landingPageReducer.ts

@@ -1,5 +1,5 @@
 import { useReducer } from "react"
-import { TopImg, TopVideo, Text, Img as ImgProps, GhButton, TopSlider, LinkButton, Shelfnew, WxAutoButton, Floatbutton } from '../../req'
+import { TopImg, TopVideo, Text, Img as ImgProps, GhButton, TopSlider, WxAutoButton, Floatbutton, ImageText } from '../../req'
 
 type Content = {
     seat: string,
@@ -17,7 +17,7 @@ type Props = {
 }
 
 interface State {
-    content: Array<TopImg | TopVideo | Content | Text | ImgProps | GhButton | WxAutoButton | TopSlider | LinkButton | Shelfnew>,    // 内容
+    content: Array<TopImg | TopVideo | Content | Text | ImgProps | GhButton | WxAutoButton | TopSlider | ImageText>,    // 内容
     pageBackColor: string  // 背景色
 }
 interface Action {

+ 55 - 61
src/pages/launchSystemNew/components/addLandingPage/sortable.tsx

@@ -171,36 +171,72 @@ export const SortableItemJumpLink = SortableElement(({ item, click, del, data, h
 
 /** 图文 */
 export const SortableItemImgText = SortableElement(({ item, click, del, index, data, handleBtn }: any) => {
-    let { paddingTop, paddingBottom, layoutItems, borderColor, bgColor, type, wxad_align } = item
-    if (type === '104') {
-        let componentItem = layoutItems?.componentItem[0]?.layoutItems?.componentItem
-        let otherData = componentItem[1]?.layoutItems?.componentItem
+    let { paddingTop, paddingBottom, imageTextItem, alignMode } = item
+    if (imageTextItem.length === 2) {
+        return <div className={`compt componentType103 ${item.comptActive && 'comptActive'}`} onClick={(e) => { click(e) }}>
+            <div className={'componentWrap'}>
+                <div className={'componentContent'}>
+                    <div className='shelf gridType' style={{ marginTop: paddingTop / 2 + 'px', marginBottom: paddingBottom / 2 + 'px', marginLeft: 20 }}>
+                        {imageTextItem?.map((item: any, index1: number) => {
+                            let { bgColor, borderColor, desc, descColor, imageUrl, title, titleColor, content } = item
+                            let { btnTitle, btnBgColorTheme, btnBorderColorTheme, fontColor, btnFontType } = content
+                            return <div className='shelfItem-3q' key={index1} style={{ borderWidth: 1, borderStyle: 'solid', borderColor: borderColor, backgroundColor: bgColor || 'rgb(255,255,255)', marginLeft: index1 === 1 ? 11 : 0 }}>
+                                <div className='shelfItemImg' style={{ marginLeft: 5.5, marginTop: 5.5 }}>
+                                    {imageUrl ? <img src={imageUrl} style={{ display: 'flex', width: '100%', height: '100%' }} /> : <div className="default" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+                                        <div className={'defaultIcon'} style={{ width: 36, height: 36 }}>
+                                            <Img />
+                                        </div>
+                                    </div>}
+                                </div>
+                                <div className='shelfItemContent' style={{ padding: '0 12px', boxSizing: 'border-box', textAlign: alignMode === 0 ? 'left' : 'center' }}>
+                                    <p className='title' style={{ color: titleColor || 'rgb(53, 53, 53)', fontSize: 16, marginBottom: 4 }}>{title || '标题'}</p>
+                                    <p className='desc' style={{ color: descColor || 'rgb(178, 178, 178)', marginBottom: 14 }}>{desc || '描述'}</p>
+                                    <p className='btn' style={{
+                                        textDecoration: 'none',
+                                        fontWeight: btnFontType == '1' ? 'bold' : 400,
+                                        color: fontColor || 'rgb(255, 255, 255)',
+                                        backgroundColor: btnBgColorTheme || 'rgb(7,193,96)',
+                                        borderStyle: 'solid',
+                                        borderColor: btnBorderColorTheme,
+                                        borderRadius: 4
+                                    }}>{btnTitle}</p>
+                                </div>
+                            </div>
+                        })}
+                    </div>
+                </div>
+            </div>
+            <ComptEdit data={data} handleBtn={(type, index) => { handleBtn(type, index) }} del={(e) => { del(e) }} />
+        </div>
+    } else {
+        let { bgColor, borderColor, desc, descColor, imageUrl, title, titleColor, content } = imageTextItem[0]
+        let { btnTitle, btnBgColorTheme, btnBorderColorTheme, fontColor, btnFontType } = content
         return <div className={`compt componentType104 ${item.comptActive && 'comptActive'}`} onClick={(e) => { click(e) }}>
             <div className={'componentWrap'}>
                 <div className={'componentContent'}>
                     <div className={'shelf listType'} style={{ marginTop: paddingTop / 2 + 'px', marginBottom: paddingBottom / 2 + 'px', marginLeft: 20, marginRight: 20 }}>
                         <div className={'shelfItem'} style={{ border: `1px solid ${borderColor || "#e5e5e5"}`, backgroundColor: bgColor || "#ffffff" }}>
                             <div className={'shelfItemImg'} style={{ marginLeft: 11.5, marginTop: 11.5 }}>
-                                {componentItem[0]?.pureImageUrl ? <img src={componentItem[0]?.pureImageUrl} style={{ display: 'flex', width: '100%', height: '100%' }} /> : <div className={'default'} style={{ width: '100%', height: '100%' }}>
+                                {imageUrl ? <img src={imageUrl} style={{ display: 'flex', width: '100%', height: '100%' }} /> : <div className={'default'} style={{ width: '100%', height: '100%' }}>
                                     <div className={'defaultIcon'} style={{ marginTop: 27, width: 36, height: 36 }}>
                                         <Img />
                                     </div>
                                 </div>}
                             </div>
                             <div className={'shelfItemContent'} style={{ margin: '12px 20px 0 12px' }}>
-                                <p className={'title'} style={{ color: otherData[0]?.fontColor || "#353535", fontSize: 16 }}>{otherData[0]?.content || otherData[0]?.name}</p>
-                                <p className={'desc'} style={{ color: otherData[1]?.fontColor || "#B2B2B2" }}>{otherData[1]?.content || otherData[1]?.name}</p>
+                                <p className={'title'} style={{ color: titleColor || "#353535", fontSize: 16 }}>{title || '标题'}</p>
+                                <p className={'desc'} style={{ color: descColor || "#B2B2B2" }}>{desc || '描述'}</p>
                                 <div
                                     className={'btn'}
                                     style={{
-                                        color: otherData[2]?.fontColor || 'rgb(255, 255, 255)',
+                                        color: fontColor || 'rgb(255, 255, 255)',
                                         textDecoration: 'none',
-                                        fontWeight: otherData[2]?.btnFontType === '0' ? 'normal' : 'bold',
-                                        backgroundColor: otherData[2]?.btnBgColorTheme || "#07C160",
-                                        border: ['#FFFFFF', '#ffffff', 'rgb(255, 255, 255)'].indexOf(otherData[2]?.btnBorderColorTheme) !== -1 ? '0px solid rgb(255, 255, 255)' : `2px solid ${otherData[2]?.btnBorderColorTheme}`,
+                                        fontWeight: btnFontType == '1' ? 'bold' : 'normal',
+                                        backgroundColor: btnBgColorTheme || "#07C160",
+                                        border: ['#FFFFFF', '#ffffff', 'rgb(255, 255, 255)'].indexOf(btnBorderColorTheme) !== -1 ? '0px solid rgb(255, 255, 255)' : `2px solid ${btnBorderColorTheme}`,
                                         borderRadius: 4
                                     }}
-                                >{otherData[2]?.btnTitle}</div>
+                                >{btnTitle}</div>
                             </div>
                         </div>
                     </div>
@@ -208,49 +244,7 @@ export const SortableItemImgText = SortableElement(({ item, click, del, index, d
             </div>
             <ComptEdit data={data} handleBtn={(type, index) => { handleBtn(type, index) }} del={(e) => { del(e) }} />
         </div>
-    } else if (type === '103') {
-        let componentItem = layoutItems?.componentItem
-        return <div className={`compt componentType103 ${item.comptActive && 'comptActive'}`} onClick={(e) => { click(e) }}>
-            <div className={'componentWrap'}>
-                <div className={'componentContent'}>
-                    <div className='shelf gridType' style={{ marginTop: paddingTop / 2 + 'px', marginBottom: paddingBottom / 2 + 'px', marginLeft: 20 }}>
-                        {
-                            componentItem?.map((item: any, index: number) => {
-                                let shelfnewItem = item?.layoutItems?.componentItem[0]?.layoutItems?.componentItem
-                                return <div className='shelfItem-3q' key={index} style={{ borderWidth: 1, borderStyle: 'solid', borderColor: item?.borderColor, backgroundColor: item?.bgColor || 'rgb(255,255,255)', marginLeft: index === 1 ? 11 : 0 }}>
-                                    <div className='shelfItemImg' style={{ marginLeft: 5.5, marginTop: 5.5 }}>
-                                        {shelfnewItem[0]?.pureImageUrl ? <img src={shelfnewItem[0]?.pureImageUrl} style={{ display: 'flex', width: '100%', height: '100%' }} /> : <div className="default" style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
-                                            <div className={'defaultIcon'} style={{ width: 36, height: 36 }}>
-                                                <Img />
-                                            </div>
-                                        </div>}
-                                    </div>
-                                    <div className='shelfItemContent' style={{ marginLeft: 12, textAlign: wxad_align === 0 ? 'left' : 'center' }}>
-                                        <p className='title' style={{ color: shelfnewItem[1]?.fontColor || 'rgb(53, 53, 53)', fontSize: 16, marginBottom: 4 }}>{shelfnewItem[1]?.content || shelfnewItem[1]?.name}</p>
-                                        <p className='desc' style={{ color: shelfnewItem[2]?.fontColor || 'rgb(178, 178, 178)', marginBottom: 14 }}>{shelfnewItem[2]?.content || shelfnewItem[2]?.name}</p>
-                                        <p className='btn' style={{
-                                            textDecoration: 'none',
-                                            fontWeight: shelfnewItem[3]?.btnFontType === '0' ? 400 : 'bold',
-                                            color: shelfnewItem[3]?.fontColor || 'rgb(255, 255, 255)',
-                                            backgroundColor: shelfnewItem[3]?.btnBgColorTheme || 'rgb(7,193,96)',
-                                            borderWidth: shelfnewItem[3]?.borderSize ? Number(shelfnewItem[3]?.borderSize) : 0,
-                                            borderStyle: 'solid',
-                                            borderColor: shelfnewItem[3]?.btnBorderColorTheme,
-                                            borderRadius: 4
-                                        }}>{shelfnewItem[3]?.btnTitle}</p>
-                                    </div>
-                                </div>
-                            })
-                        }
-                    </div>
-                </div>
-            </div>
-            <ComptEdit data={data} handleBtn={(type, index) => { handleBtn(type, index) }} del={(e) => { del(e) }} />
-        </div>
-    } else {
-        return null
     }
-
 })
 
 /** 悬浮组件 */
@@ -261,18 +255,18 @@ export const SortableItemFloatbutton = SortableElement(({ item, click, del }: an
             <div className="componentContent">
                 <div className="floatButtonWrapper">
                     <div className="floatButton">
-                        {styleType === 0 && (imageUrl ? <img src={imageUrl} className="floatButtonAvatar"/> : <div className="floatButtonAvatarPlaceholder"></div>)}
+                        {styleType === 0 && (imageUrl ? <img src={imageUrl} className="floatButtonAvatar" /> : <div className="floatButtonAvatarPlaceholder"></div>)}
                         <div className="floatButtonTexts">
                             <div className="floatButtonTitle" style={{ color: titleColor || 'rgb(23, 23, 23)' }}>{title || '标题'}</div>
-                            {(styleType === 1 || styleType === 0 ) && <div className="floatButtonDesc" style={{ color: descColor || 'rgb(76, 76, 76)' }}>{desc || '描述'}</div>}
+                            {(styleType === 1 || styleType === 0) && <div className="floatButtonDesc" style={{ color: descColor || 'rgb(76, 76, 76)' }}>{desc || '描述'}</div>}
                         </div>
                         <div className="floatButtonLink" style={{
-                            color: componentItem?.fontColor || 'rgb(255,255,255)', 
-                            fontWeight: componentItem?.btnFontType === 0 ? 'normal' : 'bold', 
-                            backgroundColor: componentItem?.btnBgColorTheme || 'rgb(7, 193, 96)', 
+                            color: componentItem?.fontColor || 'rgb(255,255,255)',
+                            fontWeight: componentItem?.btnFontType === 0 ? 'normal' : 'bold',
+                            backgroundColor: componentItem?.btnBgColorTheme || 'rgb(7, 193, 96)',
                             width: ((componentItem?.layoutWidth || 160) / 2) + 'px',
-                            textAlign: 'center', 
-                            overflow: 'hidden', 
+                            textAlign: 'center',
+                            overflow: 'hidden',
                             whiteSpace: 'pre'
                         }}>{componentItem?.btnTitle}</div>
                     </div>

+ 6 - 1
src/pages/launchSystemNew/components/customerServiceModal/index.tsx

@@ -22,7 +22,7 @@ const CustomerServiceModal: React.FC<Props> = (props) => {
     const [tableData, setTableData] = useState<any[]>([])//table数据
     const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
     const [selectCrop, setSelectCrop] = useState<number>(1)   // 选择按钮
-    const [data, setData] = useState<any>(data1)
+    const [data, setData] = useState<any>(JSON.parse(JSON.stringify(data1)))
     const [visible, setVisible] = useState<boolean>(false)
     const [sysVisible, setSysVisible] = useState<boolean>(false)
     const [corpId, setCorpId] = useState<string>('')
@@ -32,6 +32,10 @@ const CustomerServiceModal: React.FC<Props> = (props) => {
     const sysCropWechat = useAjax((params) => sysCropWechatApi(params))
     /************************/
 
+    useEffect(() => {
+        setData(data1)
+    }, [data1])
+
     /** 未配置账户获取 */
     useEffect(() => {
         setNotConfigured(() => data.filter((item: { data: { cropList: any[] }[] }) => item?.data?.some((item: { cropList: any[] }) => item?.cropList?.length === 0)))
@@ -117,6 +121,7 @@ const CustomerServiceModal: React.FC<Props> = (props) => {
                 </Tooltip> : <Button type="primary" onClick={handleOk}>确定</Button>}
             </Space>}
             width={1100}
+            onCancel={() => setVisible(false)}
             className={style.SelectPackage}
             bodyStyle={{ padding: '0 10px 0 10px' }}
         >

+ 3 - 2
src/pages/launchSystemNew/components/dataSourceModal/index.tsx

@@ -13,6 +13,7 @@ import columns from './tableConfig'
  * @returns 
  */
 interface Props {
+    promotedObjectType?: string
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
@@ -21,7 +22,7 @@ interface Props {
 const DataSourceModal: React.FC<Props> = (props) => {
 
     /************************/
-    const { visible, onClose, data: data1, onChange } = props
+    const { visible, onClose, data: data1, onChange, promotedObjectType } = props
     const [tableData, setTableData] = useState<any[]>([])//table数据
     const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
     const [data, setData] = useState<any>(data1)
@@ -40,7 +41,7 @@ const DataSourceModal: React.FC<Props> = (props) => {
 
     // 获取数据源
     const getList = (params: number[]) => {
-        getDataSource.run(params).then(res => {
+        getDataSource.run({ accountIds: params, promotedObjectType }).then(res => {
             console.log('res===>', res)
             if (res && Object.keys(res)?.indexOf(params[0].toString()) !== -1) {
                 setTableData(res[params[0]]?.map((item: { userActionSetId: string }) => ({ ...item, id: item?.userActionSetId })))

+ 10 - 2
src/pages/launchSystemNew/components/goodsModal/index.tsx

@@ -75,6 +75,13 @@ const GoodsModal: React.FC<Props> = (props) => {
         setData([...newData])
     }
 
+    // 清空已选
+    const clearGoods = () => {
+        let newData = JSON.parse(JSON.stringify(data))
+        newData[selectAdz - 1]['productList'] = []
+        setData([...newData])
+    }
+
     return <Modal
         title={<Space>
             <span>商品库</span>
@@ -97,8 +104,9 @@ const GoodsModal: React.FC<Props> = (props) => {
                     </div>))}
             </div>
             <div className={style.right}>
-                <Space style={{ marginBottom: 10 }} align="end">
-                    <Button icon={<SyncOutlined />} type='link' loading={getGoods?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
+                <Space style={{ marginBottom: 10 }} align="end" size={2}>
+                    <Button icon={<SyncOutlined />} type='link' loading={getGoods?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}>刷新</Button>
+                    {data[selectAdz - 1]?.productList?.length > 0 && <Button type='link' onClick={() => { clearGoods() }}>清空</Button>}
                 </Space>
                 <Tables
                     columns={columns()}

+ 1 - 1
src/pages/launchSystemNew/components/previewOrigin/index.tsx

@@ -25,7 +25,7 @@ const PreviewOrigin: React.FC<Props> = (props) => {
             window.open(res)
         })
     }
-    return <Button style={{ fontSize: 12, color: '#1890FF', padding: 0 }} loading={getAdcreativeTemplatePreviews.loading} type='link' onClick={() => handleVisibleChange()}>创意预览</Button>
+    return <Button style={{ fontSize: 12, color: '#1890FF', padding: 0, lineHeight: 'normal', height: 'auto' }} loading={getAdcreativeTemplatePreviews.loading} type='link' onClick={() => handleVisibleChange()}>创意预览</Button>
 }
 
 

+ 19 - 16
src/pages/launchSystemNew/components/selectCloud/index.tsx

@@ -63,22 +63,25 @@ const SelectCloud: React.FC<Props> = (props) => {
         title={<Space>
             <span>选择素材</span>
             {(selectItem && num && selectItem?.length > 0 && num > 1) && <div className={style.selected}>
-                <Image.PreviewGroup>
-                    {
-                        selectItem?.map((item: any, index: number) => (
-                            <div key={index} className={style.antImg}>
-                                <CloseCircleFilled className={style.clear} onClick={() => { clearImg(item?.id) }} />
-                                <Image
-                                    className={style.imgAnt}
-                                    width={35}
-                                    height={35}
-                                    src={item?.url}
-                                />
-                            </div>
-                        ))
-                    }
-
-                </Image.PreviewGroup>
+                {mediaType === 'IMG' ? <div>
+                    <Image.PreviewGroup>
+                        {selectItem?.filter((item: any, index: number) => index + 1 <= 10)?.map((item: any, index: number) => (<div className={style.antImg} key={index}>
+                            <CloseCircleFilled className={style.clear} onClick={() => { clearImg(item?.id) }} />
+                            <Image
+                                className={style.imgAnt}
+                                width={32}
+                                height={32}
+                                src={item?.url}
+                            />
+                        </div>))}
+                    </Image.PreviewGroup>
+                    {selectItem.length > 10 && <span>...</span>}
+                </div> : selectItem?.map((item: any, index: number) => (
+                    <div key={index} className={style.antImg}>
+                        <CloseCircleFilled className={style.clear} onClick={() => { clearImg(item?.id) }} />
+                        <video className={style.imgAnt} src={item?.url} height={30} />
+                    </div>
+                ))}
             </div>}
         </Space>}
         visible={visible}

+ 1 - 1
src/pages/launchSystemNew/components/textAideInput/index.tsx

@@ -58,7 +58,7 @@ const TextAideInput: React.FC<Props> = (props) => {
                         textList(value)
                     }}
                     onBlur={() => {
-                        // setTimeout(() => { setDescriptionshow(false) }, 100)
+                        setTimeout(() => { setDescriptionshow(false) }, 500)
                     }}
                     onChange={(e) => {
                         let value = e.target.value

+ 46 - 5
src/pages/launchSystemNew/launchManage/createAd/ad/modal/leadAd.tsx

@@ -10,6 +10,7 @@ import { CreateAdProps } from '@/services/launchAdq/createAd';
 import { createSysAdgroups } from '@/services/launchAdq/localAd';
 import AdPositionList from './adPositionList';
 import BidAdjustment from './bidAdjustment';
+import { useModel } from 'umi';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -42,6 +43,7 @@ export interface SiteSetPackageDataProps {
 /**收集线索广告弹窗*/
 function LeadAdModal(props: Props) {
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
+    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
     const createSysAdgroup = useAjax((params) => createSysAdgroups(params))
     let arg = type === 'look' ? { footer: null } : {}
     let [state, setState] = useState<any>({
@@ -264,16 +266,24 @@ function LeadAdModal(props: Props) {
             if (dataInfo?.firstDayBeginTime) {//存在首日开始时间,选中开关
                 setState({ ...state, isShowTime: ['1'] })
             }
+        } else {
+            form.setFieldsValue({ 
+                adgroupName: '广告_销售线索_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId,
+                date: moment().startOf('day').add(4, 'M'),
+                optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
+                bidStrategy: "BID_STRATEGY_TARGET_COST",
+                bidAmount: '1000'
+            })
         }
     }, [dataInfo])
     // 出价方式改变清空某些数据
     const bidModeChange = useCallback((props) => {
         form.setFieldsValue({
             ...props,
-            optimizationGoal: null,
-            smartBidType: null,
+            optimizationGoal: 'OPTIMIZATIONGOAL_ECOMMERCE_ORDER',
+            smartBidType: 'SMART_BID_TYPE_CUSTOM',
             // bidAmount:null,
-            bidStrategy: null,
+            bidStrategy: 'BID_STRATEGY_TARGET_COST',
             autoAcquisitionEnabled: false,
             autoAcquisitionBudget: null,
             dailyBudget: null,
@@ -472,7 +482,14 @@ function LeadAdModal(props: Props) {
             {/* ============================================================排期与出价============================================================= */}
             <Divider orientation='center'>排期与出价</Divider>
             <Form.Item label={<strong>投放日期</strong>} name='dateType'>
-                <Radio.Group >
+                <Radio.Group onChange={(e) => {
+                    if (e.target.value === "1") {
+                        form.setFieldsValue({ date: [moment().startOf('day').add(4, 'M'), moment().startOf('day').add(12, 'M')] })
+                    }
+                    if (e.target.value === "2") {
+                        form.setFieldsValue({ date: moment().startOf('day').add(4, 'M') })
+                    }
+                }}>
                     <Radio.Button value="1">选择开始与结束日期</Radio.Button>
                     <Radio.Button value="2">长期投放</Radio.Button>
                 </Radio.Group>
@@ -500,7 +517,31 @@ function LeadAdModal(props: Props) {
                 </Form.Item>}
             </Form.Item>
             <Form.Item label={<strong>出价方式<Tooltip title='出价方式不同将影响自定义人群,行为兴趣意向等某些功能无法使用'><ExclamationCircleOutlined style={{ color: '#e91e63', marginLeft: 5 }} /></Tooltip></strong>} name='bidMode' rules={[{ required: true, message: '请选择出价方式' }]}>
-                <Radio.Group onChange={bidModeChange}>
+                <Radio.Group onChange={(e) => {
+                    console.log(e.target.value);
+                    
+                    if (e.target.value === "BID_MODE_CPM" || e.target.value === "BID_MODE_CPC") {
+                        form.setFieldsValue({
+                            optimizationGoal: null,
+                            smartBidType: null,
+                            // bidAmount:null,
+                            bidStrategy: null,
+                            autoAcquisitionEnabled: false,
+                            autoAcquisitionBudget: null,
+                            dailyBudget: null,
+                        })
+                    } else {
+                        form.setFieldsValue({
+                            optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
+                            smartBidType: "SMART_BID_TYPE_CUSTOM",
+                            bidAmount: '1000',
+                            bidStrategy: "BID_STRATEGY_TARGET_COST",
+                            autoAcquisitionEnabled: false,
+                            autoAcquisitionBudget: null,
+                            dailyBudget: null,
+                        })
+                    }
+                }}>
                     {
                         Object.keys(BidModeEnum).filter(key => { if (siteSet?.some((name: string) => name === "SITE_SET_MOMENTS")) { return key === 'BID_MODE_OCPM' || key === 'BID_MODE_CPM' } else { return true } })?.map(key => {
                             return <Radio.Button value={key} key={key} >{BidModeEnum[key]}</Radio.Button>

+ 62 - 4
src/pages/launchSystemNew/launchManage/createAd/ad/modal/wechat.tsx

@@ -11,6 +11,7 @@ import { createSysAdgroups } from '@/services/launchAdq/localAd';
 import AdPositionList from './adPositionList';
 import { SiteSetPackageDataProps } from './leadAd';
 import BidAdjustment from './bidAdjustment';
+import { useModel } from 'umi';
 const { RangePicker }: { RangePicker: any } = DatePicker;
 let DatePickers: any = DatePicker
 interface Props {
@@ -28,6 +29,7 @@ function WeChatAdModal(props: Props) {
 
     /*******************************/
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm, ajax } = props
+    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
     const createSysAdgroup = useAjax((params) => createSysAdgroups(params))
     let [state, setState] = useState<any>({ isShowTime: [] })
     let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
@@ -116,7 +118,7 @@ function WeChatAdModal(props: Props) {
                         if (newValues[key] === 'DEEP_CONVERSION_WORTH') { // 优化 ROI
                             deepConversionSpec.deepConversionWorthSpec = {
                                 goal: newValues.goal,
-                                expectedroi: newValues.deepBidAmount
+                                expectedRoi: newValues.deepBidAmount
                             }
                         } else if (newValues[key] === 'DEEP_CONVERSION_BEHAVIOR') { // 优化转化行为
                             deepConversionSpec.deepConversionBehaviorSpec = {
@@ -226,7 +228,7 @@ function WeChatAdModal(props: Props) {
                                 depthConversionEnabled: true,
                                 deepConversionType: dataInfo[key]?.deepConversionType,
                                 goal: dataInfo[key]?.deepConversionWorthSpec?.goal,
-                                deepBidAmount: dataInfo[key]?.deepConversionWorthSpec?.expectedroi,
+                                deepBidAmount: dataInfo[key]?.deepConversionWorthSpec?.expectedRoi,
                             }
                         } else if (dataInfo[key]?.deepConversionType === 'DEEP_CONVERSION_BEHAVIOR') {
                             formData = {
@@ -246,6 +248,14 @@ function WeChatAdModal(props: Props) {
             if (dataInfo?.firstDayBeginTime) {//存在首日开始时间,选中开关
                 setState({ ...state, isShowTime: ['1'] })
             }
+        } else {
+            form.setFieldsValue({
+                adgroupName: '广告_微信朋友圈_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId,
+                date: moment().startOf('day').add(4, 'M'),
+                optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
+                bidStrategy: "BID_STRATEGY_TARGET_COST",
+                bidAmount: '1000'
+            })
         }
     }, [dataInfo])
     // 出价方式改变清空某些数据
@@ -363,6 +373,24 @@ function WeChatAdModal(props: Props) {
                             <Col span={6}>
                                 <Checkbox value="SITE_SET_WECHAT">微信公众号与小程序</Checkbox>
                             </Col>
+                            {/* <Col span={5}>
+                                <Checkbox value="SITESET_WECHAT_PLUGIN">微信新闻插件</Checkbox>
+                            </Col> */}
+                            <Col span={4}>
+                                <Checkbox value="SITE_SET_MOBILE_UNION">优量汇</Checkbox>
+                            </Col>
+                            <Col span={4}>
+                                <Checkbox value="SITE_SET_TENCENT_NEWS">腾讯新闻</Checkbox>
+                            </Col>
+                            <Col span={4}>
+                                <Checkbox value="SITE_SET_TENCENT_VIDEO">腾讯视频</Checkbox>
+                            </Col>
+                            <Col span={4}>
+                                <Checkbox value="SITE_SET_KANDIAN">QQ 浏览器</Checkbox>
+                            </Col>
+                            <Col span={6}>
+                                <Checkbox value="SITE_SET_QQ_MUSIC_GAME">QQ、腾讯音乐及游戏</Checkbox>
+                            </Col>
                         </Row>
                     </Checkbox.Group>
                 </Form.Item>}
@@ -403,7 +431,14 @@ function WeChatAdModal(props: Props) {
             {/* ============================================================排期与出价============================================================= */}
             <Divider orientation='center'>排期与出价</Divider>
             <Form.Item label={<strong>投放日期</strong>} name='dateType'>
-                <Radio.Group >
+                <Radio.Group onChange={(e) => {
+                    if (e.target.value === "1") {
+                        form.setFieldsValue({ date: [moment().startOf('day').add(4, 'M'), moment().startOf('day').add(12, 'M')] })
+                    }
+                    if (e.target.value === "2") {
+                        form.setFieldsValue({ date: moment().startOf('day').add(4, 'M') })
+                    }
+                }}>
                     <Radio.Button value="1">选择开始与结束日期</Radio.Button>
                     <Radio.Button value="2">长期投放</Radio.Button>
                 </Radio.Group>
@@ -431,7 +466,30 @@ function WeChatAdModal(props: Props) {
                 </Form.Item>}
             </Form.Item>
             <Form.Item label={<strong>出价方式<Tooltip title='oCPC/oCPM出价,或开启自动扩量/智能扩量时不支持二方人群'><ExclamationCircleOutlined style={{ color: '#e91e63', marginLeft: 5 }} /></Tooltip></strong>} name='bidMode' rules={[{ required: true, message: '请选择出价方式' }]}>
-                <Radio.Group onChange={bidModeChange}>
+                <Radio.Group onChange={(e) => {
+                    if (e.target.value === "BID_MODE_CPM") {
+                        form.setFieldsValue({
+                            optimizationGoal: null,
+                            smartBidType: null,
+                            // bidAmount:null,
+                            bidStrategy: null,
+                            autoAcquisitionEnabled: false,
+                            autoAcquisitionBudget: null,
+                            dailyBudget: null,
+                        })
+                    } else {
+                        form.setFieldsValue({
+                            optimizationGoal: "OPTIMIZATIONGOAL_ECOMMERCE_ORDER",
+                            smartBidType: "SMART_BID_TYPE_CUSTOM",
+                            bidAmount: '1000',
+                            bidStrategy: "BID_STRATEGY_TARGET_COST",
+                            autoAcquisitionEnabled: false,
+                            autoAcquisitionBudget: null,
+                            dailyBudget: null,
+                        })
+                    }
+
+                }}>
                     {
                         Object.keys(BidModeEnum).filter(key => { if (siteSet?.some((name: string) => name === "SITE_SET_MOMENTS")) { return key === 'BID_MODE_OCPM' || key === 'BID_MODE_CPM' } else { return true } })?.map(key => {
                             return <Radio.Button value={key} key={key} >{BidModeEnum[key]}</Radio.Button>

+ 3 - 3
src/pages/launchSystemNew/launchManage/createAd/creative/index.tsx

@@ -1,5 +1,5 @@
 import React, { useCallback, useState } from 'react'
-import { Button, Col, Space, Spin, Tabs, Tooltip, } from 'antd'
+import { Col, Space, Spin, Tabs, Tooltip, } from 'antd'
 import style from '../index.less'
 import CreativeModal from "../../../components/creativeModal"
 import { CreateAdProps } from '@/services/launchAdq/createAd'
@@ -19,7 +19,7 @@ type Props = {
     set_targetKey: (key: string) => void
 }
 function Creative(props: Props) {
-    let { queryForm, getSysAdgroups, getSysAdcreative, setQueryForm, clearData, targetKey, set_targetKey, page_checked } = props
+    let { queryForm, getSysAdgroups, getSysAdcreative, setQueryForm, clearData, targetKey, set_targetKey } = props
     const [adModalConfig, setAdModalConfig] = useState<ModalConfig>({//新建广告弹窗
         visible: false,
         type: 'add'
@@ -81,7 +81,7 @@ function Creative(props: Props) {
                         return <Tabs.TabPane key={index} tab={'创意' + (index + 1)}>
                             <Spin spinning={getSysAdcreative.loading}>
                                 <div className={style.centerContent}>
-                                    {item.sysAdcreative && <AdcreativeCol data={item.sysAdcreative} />}
+                                    {item?.sysAdcreative && <AdcreativeCol data={item.sysAdcreative} />}
                                 </div>
                             </Spin>
                         </Tabs.TabPane >

+ 53 - 41
src/pages/launchSystemNew/launchManage/createAd/creative/modal/index.tsx

@@ -14,6 +14,7 @@ import { createSysAdcreative } from '@/services/launchAdq/creative'
 import { creativeConfig, overrideCanvasHeadOptionEnum } from './config'
 import BrandImage from './brandImage'
 import HeadNickJump from './headNickJump'
+import moment from 'moment'
 interface Props {
     queryForm: Partial<CreateAdProps>,
     title?: string,
@@ -28,6 +29,7 @@ interface Props {
 /**创意模板*/
 function CreativePup(props: Props) {
     let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm } = props
+    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
     let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
     let { promotedObjectType, sysAdgroup } = queryForm
     let { siteSet } = sysAdgroup
@@ -49,6 +51,7 @@ function CreativePup(props: Props) {
     const [endPageDescShow, setendPageDescnshow] = useState(false)
     const [isShowSc, set_isShowSc] = useState(false)//是否展示素材选项
     const [infoSet, set_infoSet] = useState(false)//回填设置已完成
+    const [isShowXd, setIsShowXd] = useState(false)
     const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
         activeUrl: string,//选中的视频封面图地址
         preview: boolean,//是否开启图片点击预览
@@ -275,24 +278,18 @@ function CreativePup(props: Props) {
                 campaignType: 'CAMPAIGN_TYPE_NORMAL',
             }).then(res => {
                 let newArr: any = []
+                let newData: any[] = []
                 // 过滤掉相同的和即将下线的
                 if (!res) {
                     return
                 }
                 // 
                 Object.values(res)?.forEach((arr: any) => {
+                    newData.push(arr)
                     Array.isArray(arr) && arr?.forEach((item: any) => {
                         if (newArr.length > 0) {//假如已存在ID,需要过滤相同
                             if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {//不重复的添加
                                 newArr.push(item)
-                            } else {
-                                // 找出通用创意
-                                newArr = newArr?.map((arr: { adcreativeTemplateId: any }) => {
-                                    if (arr.adcreativeTemplateId === item.adcreativeTemplateId) {
-                                        return { ...arr, isGeneral: true }
-                                    }
-                                    return arr
-                                })
                             }
                         } else {//不存在ID直接过滤掉即将下线的
                             if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
@@ -305,12 +302,21 @@ function CreativePup(props: Props) {
                 if (siteSet.some((i: string) => i === 'SITE_SET_MOMENTS')) {
                     newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 910 && item.adcreativeTemplateId !== 925)
                 }
-                set_adcreative_template_list(newArr)
+                let newArr1: any[] = []
+                let newArr2: any[] = []
+                newArr?.forEach((arr: { adcreativeTemplateId: any, isGeneral?: boolean }) => {
+                    if (newData.every((item: { adcreativeTemplateId: any }[]) => item.find(i => i.adcreativeTemplateId === arr.adcreativeTemplateId))) {
+                        newArr1.push({ ...arr, isGeneral: true })
+                    } else {
+                        newArr2.push(arr)
+                    }
+                })
+                set_adcreative_template_list([...newArr1, ...newArr2])
             })
         }
     }, [siteSet, promotedObjectType])
     // 获取创意形式详情
-    const getTemplate = useCallback((id: any, ok?: any) => {
+    const getTemplate = (id: any, ok?: any) => {
         // CAMPAIGN_TYPE_NORMAL
         if (siteSet?.length > 0 && promotedObjectType && id) {
             if (id) {
@@ -320,6 +326,7 @@ function CreativePup(props: Props) {
                     adcreativeTemplateId: id
                 }).then(res => {
                     if (res?.length > 0) {
+                        form.setFieldsValue({ adcreativeName: res[0]?.adcreativeTemplateAppellation + '_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId })
                         set_adcreative_template(res[0])
                         if (siteSet?.some((name: string) => name === 'SITE_SET_MOMENTS')) {
                             let id = res[0].adcreativeTemplateId
@@ -333,7 +340,7 @@ function CreativePup(props: Props) {
                 })
             }
         }
-    }, [siteSet, promotedObjectType])
+    }
     // 获取对应落地页按钮
     const pageTypeList = useMemo(() => {
         if (adcreativeTemplateId) {
@@ -370,7 +377,7 @@ function CreativePup(props: Props) {
     }, [adcreative_template_list])
 
     //每次选中创意设置该展示的界面
-    const templateChange = useCallback((adcreative_template, ok?: any) => {
+    const templateChange = (adcreative_template: any, ok?: any) => {
         let states = {
             kp_show: false,
             xd_show: true,
@@ -402,9 +409,10 @@ function CreativePup(props: Props) {
 
             //行动按钮组件存在
             if (states.xd_show) {
-                let linkNameList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
+                let supportLinkNameTypeData = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType
+                let linkNameList = supportLinkNameTypeData?.list
                 let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
-                if (linkNameList && !linkPageType) {
+                if (linkNameList) {
                     if (!ok) {
                         let linkNameType = linkNameList[0]?.linkNameType
                         let linkPageType = linkPageList?.some((i: { linkPageType: string }) => i.linkPageType === "LINK_PAGE_TYPE_CANVAS_WECHAT") ? "LINK_PAGE_TYPE_CANVAS_WECHAT" : linkPageList[0]?.linkPageType
@@ -413,6 +421,13 @@ function CreativePup(props: Props) {
                 } else {
                     states = { ...states, xd_show: false }
                 }
+                if (supportLinkNameTypeData?.required) {
+                    states = { ...states, xd_show: true }
+                    values = { ...values, actionBtn: true }
+                    setIsShowXd(true)
+                } else {
+                    setIsShowXd(false)
+                }
             }
             // 特殊行动按钮
             if (adcreative_template.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text') && !ok) {
@@ -429,7 +444,7 @@ function CreativePup(props: Props) {
             setPupState(states)
             form.setFieldsValue(values)
         }
-    }, [pageType, linkPageType])
+    }
     // 版位改变清空数据
     useEffect(() => {
         if (imgMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== imgMaterialConfig.adcreativeTemplateId) {
@@ -479,7 +494,6 @@ function CreativePup(props: Props) {
                 adcreativeTemplateId,
             }
             getTemplate(adcreativeTemplateId, true)
-            console.log(2222,dataInfo)
             if ([720, 721, 618, 1708, 722, 1529].some(n => n === adcreativeTemplateId)) {
                 obj = { ...obj, adcreativeElementsType: '视频' }
             } else {
@@ -522,10 +536,10 @@ function CreativePup(props: Props) {
                 obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
             }
             if (profile && profile.headImageUrl && profile.profileName && profile.description) {
-                obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description}
+                obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description }
             }
-            if(buttonText){
-                obj = {...obj,buttonText}
+            if (buttonText) {
+                obj = { ...obj, buttonText }
             }
             if (videoUrl) {
                 setVideoMaterialConfig({
@@ -625,12 +639,6 @@ function CreativePup(props: Props) {
                 }
             }
         >
-            {/* ============================================================基本信息============================================================= */}
-            <Divider orientation='center'>基本信息</Divider>
-            {/* ============================================================创意名称============================================================= */}
-            <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
-                <Input placeholder='创意名称' style={{ width: 300 }} />
-            </Form.Item>
             {/* ============================================================创意形式============================================================= */}
             <Divider orientation='center'>创意形式</Divider>
             {/* ============================================================创意形式============================================================= */}
@@ -652,18 +660,16 @@ function CreativePup(props: Props) {
                                 let id = e.target.value
                                 getTemplate(id)
                             }}>
-                                {
-                                    adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)?.map((item: any) => {
-                                        return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
-                                            <div className={styles.adcreative_template_item}>
-                                                {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
-                                                <img src={item.adcreativeSampleImage} />
-                                                <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
-                                                <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
-                                            </div>
-                                        </Radio.Button>
-                                    })
-                                }
+                                {adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType && item.supportBidModeList.includes(queryForm?.sysAdgroup?.bidMode))?.map((item: any) => {
+                                    return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
+                                        <div className={styles.adcreative_template_item}>
+                                            {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
+                                            <img src={item.adcreativeSampleImage} />
+                                            <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
+                                            <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
+                                        </div>
+                                    </Radio.Button>
+                                })}
                             </Radio.Group>
                         </Form.Item>
                         {/* ============================================================创意内容============================================================= */}
@@ -889,8 +895,8 @@ function CreativePup(props: Props) {
                         }
                         {/* ============================================================普通行动按钮============================================================= */}
                         {
-                            pupState.xd_show && <Form.Item label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
-                                <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                            pupState.xd_show && <Form.Item hidden={isShowXd} label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
+                                <Switch checkedChildren="开启" unCheckedChildren="关闭" disabled={isShowXd}/>
                             </Form.Item>
                         }
                         {
@@ -938,14 +944,14 @@ function CreativePup(props: Props) {
                         }
                         {/* ============================================================特殊行动按钮============================================================= */}
                         {
-                            adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
+                            !pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
                                 <Form.Item valuePropName="checked" noStyle >
                                     <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={true} disabled defaultChecked={true} />
                                 </Form.Item>
                             </Form.Item>
                         }
                         {
-                            adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
+                            !pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
                                 <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
                                     (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                                 } allowClear>
@@ -1035,6 +1041,12 @@ function CreativePup(props: Props) {
                         }
                     </>
             }
+            {/* ============================================================基本信息============================================================= */}
+            <Divider orientation='center'>基本信息</Divider>
+            {/* ============================================================创意名称============================================================= */}
+            <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
+                <Input placeholder='创意名称' style={{ width: 300 }} />
+            </Form.Item>
         </Form>
         {/* 选择图片素材 */}
         {

+ 168 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/index.tsx

@@ -0,0 +1,168 @@
+import { CreateAdProps } from "@/services/launchAdq/createAd"
+import { BaseResult } from "@ahooksjs/use-request/lib/types"
+import { Col, Space, Spin, Tooltip } from "antd"
+import React, { useCallback, useState } from "react"
+import { ModalConfig } from "../ad"
+import style from '../index.less'
+import AdcreativeCol from "../adcreativeCol"
+import CreativePup from "./modal"
+import Material from "./modal/material"
+import Copywriting from "./modal/copywriting"
+
+
+type Props = {
+    queryForm: Partial<CreateAdProps>,
+    setQueryForm: React.Dispatch<React.SetStateAction<Partial<CreateAdProps>>>,
+    getSysAdcreative: BaseResult<any, any>,
+    clearData: () => void,
+    targetKey: string,
+}
+const CreativeCL: React.FC<Props> = (props) => {
+
+    /********************************/
+    const { queryForm, getSysAdcreative, setQueryForm, clearData, targetKey } = props
+
+    const [adModalConfig, setAdModalConfig] = useState<ModalConfig>({//新建广告弹窗
+        visible: false,
+        type: 'add'
+    })
+    /********************************/
+
+    // 设置变量
+    const handleAdModalConfig = useCallback((arg: ModalConfig) => {
+        setAdModalConfig({ ...adModalConfig, ...arg })
+    }, [adModalConfig])
+
+    return <>
+        <Col className={style.conRightBorder}>
+            <div className={style.top}>创意基本信息</div>
+            <div className={style.center}>
+                {
+                    queryForm?.taskMediaMaps?.filter((item, index) => index === 0)?.map((item, index) => {
+                        return <Spin spinning={getSysAdcreative.loading} key={index}>
+                            <div className={style.centerContent}>
+                                {item.sysAdcreative && <AdcreativeCol data={item.sysAdcreative} />}
+                            </div>
+                        </Spin>
+                    })
+                }
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.sysAdgroup ? <>
+                        <span onClick={() => { handleAdModalConfig(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative ? { visible: true, type: 'edit' } : { visible: true, type: 'add' }) }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative ? '编辑' : '添加'}</span>
+                    </> : <Tooltip title="请先设置广告">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+
+            {/* 创建创意 */}
+            {adModalConfig.visible && <CreativePup visible={adModalConfig.visible} type={adModalConfig.type} PupFn={handleAdModalConfig} callback={(values: any, material: any, textData: any[]) => {
+                let arr = queryForm.taskMediaMaps || []
+                let adqPageArr: any = queryForm.adqPageList || []
+                let pageArr: any = queryForm.pageList || []
+                adqPageArr[targetKey as string] = null//清除adq落地页
+                pageArr[targetKey as string] = null//清除本地落地页
+                arr[targetKey] = { sysAdcreative: values }
+                setQueryForm({ ...queryForm, taskMediaMaps: arr, adqPageList: adqPageArr, pageList: pageArr, materialData: material, materials: [], textData, texts: [] }); clearData();
+                handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
+            }} dataInfo={queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative} queryForm={queryForm} />}
+        </Col>
+        <Col className={style.conRightBorder}>
+            <div className={style.top}>
+                <strong style={{ fontSize: 15 }}>创意素材</strong>
+                <span>已选:{queryForm?.materials?.length || 0}</span>
+            </div>
+            <div className={style.center}>
+                <div className={queryForm?.materialData && queryForm?.materialData[0]?.name === 'image_list' && queryForm?.materialData[0]?.arrayProperty?.minNumber > 1 ? style.imgListGroup : style.otherGroup}>
+                    {queryForm?.materials?.map((item: any, index: number) => {
+                        let keys = Object.keys(item)
+                        if (keys?.includes('imageUrlList')) {
+                            return <div key={index} className={style.group}>
+                                {item?.imageUrlList?.map((url: string, index: number) => <div key={index}><img src={url} /></div>)}
+                            </div>
+                        } else {
+                            if (keys?.length > 1) {
+                                return <div key={index} className={style.otherGroup}>
+                                    {keys?.includes('imageUrl') && <div className={style.group}><img src={item.imageUrl} /></div>}
+                                    {keys?.includes('videoUrl') && <div className={style.group}><video src={item.videoUrl} /></div>}
+                                    {keys?.includes('shortVideo1Url') && <div className={style.group}><video src={item.shortVideo1Url} /></div>}
+                                </div>
+                            } else {
+                                if (keys?.includes('imageUrl')) {
+                                    return <div key={index} className={style.group}><img src={item.imageUrl} /></div>
+                                } else if (keys?.includes('videoUrl')) {
+                                    return <div key={index} className={style.group}><video src={item.videoUrl} /></div>
+                                } else if (keys?.includes('shortVideo1Url')) {
+                                    return <div key={index} className={style.group}><video src={item.shortVideo1Url} /></div>
+                                }
+                            }
+                            return null
+                        }
+                    })}
+                </div>
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.length > 0 ? <>
+                        <Material
+                            onChange={(data) => {
+                                setQueryForm({ ...queryForm, materials: data })
+                            }}
+                            material={queryForm?.materialData}
+                            value={queryForm?.materials}
+                            sysAdcreative={queryForm?.taskMediaMaps[0]?.sysAdcreative}
+                        />
+                    </> : <Tooltip title="请先设置创意">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+            {!(queryForm?.materialData && queryForm?.materialData?.length > 0) && <div className={style.noMaterial}>无需配置素材</div>}
+        </Col>
+        <Col className={style.conRightBorder} style={{ maxWidth: '25%' }}>
+            <div className={style.top}>
+                <strong style={{ fontSize: 15 }}>创意文案</strong>
+                <span>已选:{queryForm?.texts?.length || 0}</span>
+            </div>
+            <div className={style.center}>
+                {queryForm?.texts?.some((item: any) => item?.title) && <div className={style.accName} style={{ fontWeight: 800 }}>标题</div>}
+                {queryForm?.texts?.map((item: any, index: number) => {
+                    if (item?.title) {
+                        return <div className={style.accCon} key={index}><span className={style.title}>{item?.title}</span> </div>
+                    } else {
+                        return null
+                    }
+                })}
+                {queryForm?.texts?.some((item: any) => item?.description) && <div className={style.accName} style={{ fontWeight: 800 }}>广告详情</div>}
+                {queryForm?.texts?.map((item: any, index: number) => {
+                    if (item?.description) {
+                        return <div className={style.accCon} key={index}><span className={style.title}>{item?.description}</span></div>
+                    } else {
+                        return null
+                    }
+                })}
+            </div>
+            <div className={style.bottom}>
+                <Space size={20}>
+                    {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.length > 0 ? <>
+                        <Copywriting
+                            textData={queryForm.textData}
+                            sysAdcreative={queryForm?.taskMediaMaps[0]?.sysAdcreative}
+                            value={queryForm?.texts}
+                            onChange={(data) => {
+                                setQueryForm({ ...queryForm, texts: data })
+                            }}
+                        />
+                    </> : <Tooltip title="请先设置创意">
+                        <span>添加</span>
+                    </Tooltip>}
+                </Space>
+            </div>
+            {!(queryForm?.textData && queryForm.textData?.length > 0) && <div className={style.noMaterial}>无需配置文案</div>}
+        </Col>
+    </>
+}
+
+export default React.memo(CreativeCL)

+ 196 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/brandImage.tsx

@@ -0,0 +1,196 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import SelectCloud from "@/pages/launchSystemNew/components/selectCloud"
+import { addSysBrandApi, delSysBrandApi, editSysBrandApi, getSysBrandApi } from "@/services/launchAdq/global"
+import { PlusOutlined } from "@ant-design/icons"
+import { Button, Divider, Form, Input, message, Modal, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import { brandColumns } from "./tableConfig"
+
+interface Props {
+    onChange?: (data: any) => void,
+    value?: any
+}
+
+/**
+ * 品牌形象
+ * @returns 
+ */
+const BrandImage: React.FC<Props> = (props) => {
+
+    /****************************/
+    const { onChange, value } = props
+
+    const [visible, setVisible] = useState<boolean>(false)
+    const [addVisible, setAddVisible] = useState<boolean>(false)
+    const [form] = Form.useForm()
+    const [initialValues, setInitialValues] = useState<any>({})
+
+    const getSysBrand = useAjax(() => getSysBrandApi())
+    const addSysBrand = useAjax((params) => addSysBrandApi(params))
+    const editSysBrand = useAjax((params) => editSysBrandApi(params))
+    const delSysBrand = useAjax((params) => delSysBrandApi(params))
+    /****************************/
+
+    // 获取列表
+    useEffect(() => {
+        getSysBrand.run()
+    }, [])
+
+
+    // 新增修改
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (Object.keys(initialValues).length > 0) { // 修改
+            editSysBrand.run({ ...data, sysBrandId: initialValues.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    setAddVisible(false)
+                    getSysBrand.refresh()
+                }
+            })
+        } else { // 新增
+            addSysBrand.run(data).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    setAddVisible(false)
+                    getSysBrand.refresh()
+                }
+            })
+        }
+        setInitialValues({})
+    }
+
+    /** 删除 */
+    const del = (id: number) => {
+        delSysBrand.run({ sysBrandId: id }).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getSysBrand.refresh()
+            }
+        })
+    }
+
+    /** 修改 */
+    const edit = (data: any) => {
+        setInitialValues(data)
+        setAddVisible(true)
+    }
+
+    return <div>
+        <Select
+            showSearch
+            placeholder="请选择一个品牌跳转页,与广告创意一起展示"
+            optionFilterProp="children"
+            style={{ width: 400 }}
+            onChange={(e) => { onChange && onChange(e) }}
+            allowClear
+            value={value}
+            filterOption={(input, option) => {
+                return (option!.value as unknown as string).toLowerCase().includes(input.toLowerCase())
+            }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => { setAddVisible(true); setInitialValues({}) }}>新增</Button>
+                    <Button type="link" onClick={() => setVisible(true)}>前往管理</Button>
+                </div>
+            </>}
+        >
+            {
+                getSysBrand?.data?.map((item: any) => {
+                    return <Select.Option value={item.name + '_' + item.brandImgUrl} key={item.id}>
+                        <Space>
+                            <img src={item.brandImgUrl} style={{ width: 20 }} />
+                            <span>{item.name}</span>
+                        </Space>
+                    </Select.Option>
+                })
+            }
+        </Select>
+
+        {visible && <Modal title="品牌形象" width={1000} visible={visible} footer={null} onCancel={() => setVisible(false)}>
+            <Space direction='vertical' style={{ width: '100%' }}>
+                <Button type="primary" icon={<PlusOutlined />} onClick={() => { setAddVisible(true); setInitialValues({}) }}>上传品牌形象</Button>
+                <Tables
+                    columns={brandColumns(del, edit)}
+                    dataSource={getSysBrand?.data}
+                    size="small"
+                    loading={getSysBrand?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                />
+            </Space>
+        </Modal>}
+
+        {addVisible && <Modal title="上传品牌形象" visible={addVisible} confirmLoading={addSysBrand.loading} onOk={handleOk} onCancel={() => setAddVisible(false)}>
+            <Form
+                name="basic"
+                form={form}
+                layout='vertical'
+                autoComplete="off"
+                initialValues={{ ...initialValues }}
+            >
+                <Form.Item label={<strong>头像</strong>} name="brandImgUrl" rules={[{ required: true, message: '请选择头像!' }]}>
+                    <UploadImage />
+                </Form.Item>
+                <Form.Item label={<strong>名称</strong>} name="name" rules={[{ required: true, message: '请输入名称!' }]}>
+                    <Input placeholder="请输入名称" maxLength={12}/>
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </div>
+}
+
+
+interface ImageProps {
+    onChange?: (value: string) => void,
+    value?: string
+}
+/**
+ * 处理选择图片Form
+ * @returns 
+ */
+export const UploadImage: React.FC<ImageProps> = (props) => {
+
+    /*********************/
+    const { onChange, value } = props
+    const [selectImgVisible, setSelectImgVisible] = useState<boolean>(false)
+    const [sliderImgContent, setSliderImgContent] = useState<{ url: string, width?: number, height?: number }[]>([])  // 保存回填数据
+    const { init } = useModel('useLaunchAdq.useBdMediaPup')
+    /*********************/
+
+    useEffect(() => {
+        if (value) {
+            setSliderImgContent([{ url: value }])
+        } else {
+            setSliderImgContent([])
+        }
+    }, [value])
+
+    const setImg = (content: any[]) => {
+        onChange && onChange(content[0]?.url)
+        setSelectImgVisible(false)
+    }
+
+    const selectImg = () => {
+        init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: 512, height: 512 }]], maxSize: 50 * 1024 })
+        setTimeout(() => { setSelectImgVisible(true) }, 50)
+    }
+
+    return <>
+        {value ? <img src={value} onClick={selectImg} width={100} height={100} /> : <Button onClick={selectImg}>选择图片</Button>}
+        <div style={{ fontSize: 12, color: 'rgba(0,0,0,.5)' }}>
+            <div>图片尺寸:512×512 像素</div>
+            <div>图片格式:大小要求在 50KB 以内,仅支持 jpg 和 png 格式</div>
+        </div>
+
+        {/* 选择素材 */}
+        {selectImgVisible && <SelectCloud visible={selectImgVisible} sliderImgContent={sliderImgContent} onClose={() => setSelectImgVisible(false)} onChange={setImg} />}
+    </>
+}
+
+export default React.memo(BrandImage)

+ 80 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/config.ts

@@ -0,0 +1,80 @@
+/**使用外层创意素材替换原生推广页顶部素材*/
+export const overrideCanvasHeadOptionEnum = {
+  OPTION_KEEP_DIFFERENT: '自定义广告创意素材,和原生推广页顶部素材保持两者不同',
+  OPTION_CANVAS_OVERRIDE_CREATIVE: '使用原生推广页顶部素材作为外层创意素材',
+  OPTION_CREATIVE_OVERRIDE_CANVAS: '使用外层创意素材替换原生推广页顶部素材',
+};
+/**使用外层创意素材替换原生推广页顶部素材*/
+export const creativeConfig = {
+  311: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  641: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  642: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  643: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  618: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  711: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  712: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  720: {
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  721: {
+    //保持一致,替换
+    overrideCanvasHeadOption: [
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  1707: {
+    //卡片横版大图
+    overrideCanvasHeadOption: [
+      'OPTION_KEEP_DIFFERENT',
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+  1708: {
+    //卡片横版视频
+    overrideCanvasHeadOption: [
+      'OPTION_KEEP_DIFFERENT',
+      'OPTION_CANVAS_OVERRIDE_CREATIVE',
+      'OPTION_CREATIVE_OVERRIDE_CANVAS',
+    ],
+  },
+};

+ 89 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/copywriting.tsx

@@ -0,0 +1,89 @@
+import TextAideInput from "@/pages/launchSystemNew/components/textAideInput"
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
+import { Button, Collapse, Form, Modal } from "antd"
+import React, { useEffect, useState } from "react"
+import styles from './index.less'
+
+
+
+interface Props {
+    value?: any[]
+    textData?: any[],
+    sysAdcreative: any
+    onChange?: (data: any) => void
+}
+
+/**
+ * 文案设置
+ * @returns 
+ */
+const Copywriting: React.FC<Props> = (props) => {
+
+    /******************************/
+    const { onChange, textData = [], sysAdcreative, value } = props
+    const { adcreativeTemplateId } = sysAdcreative
+    const [visible, setVisible] = useState<boolean>(false)
+    const [form] = Form.useForm();
+    /******************************/
+
+    // 回填
+    useEffect(() => {
+        if (visible) {
+            if (value && value?.length > 0) {
+                form.setFieldsValue({ texts: value })
+            } else {
+                form.setFieldsValue({ texts: [undefined] })
+            }
+        }
+    }, [value, visible])
+
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            console.log('values=>1', values)
+            onChange && onChange(values?.texts)
+            setVisible(false)
+        })
+    }
+
+    return <>
+        <span onClick={() => { setVisible(true) }}>{value && value?.length > 0 ? '编辑' : '添加'}</span>
+        {visible && <Modal visible={visible} onCancel={() => setVisible(false)} title="创意文案" width={700} onOk={handleOk}>
+            <Form name="dynamic_form_item" form={form} labelAlign='left' >
+                <Form.List name="texts">
+                    {(fields, { add, remove }) => (<>
+                        <Collapse activeKey={fields.map(field => field.name)} bordered={false}>
+                            {fields.map((field, num) => (<Collapse.Panel showArrow={false} header={`文案组${num + 1}`} key={(field.name).toString()} extra={fields?.length > 1 && <MinusCircleOutlined className={styles.clear} onClick={() => remove(field.name)} style={{ color: 'red' }} />}>
+                                {textData?.map((item, index) => {
+                                    if (item.name === 'title') {
+                                        return <div key={'title' + item.fieldType}>
+                                            <Form.Item {...field} label={<strong>{item.description}(选填)</strong>} name={[field.name, item.name]} rules={[{ pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]}>
+                                                <TextAideInput placeholder={'请输入' + item.description} style={{ width: 450 }} maxTextLength={item.restriction.textRestriction.maxLength} />
+                                            </Form.Item>
+                                        </div>
+                                    }
+                                    if (item.name === 'description') {
+                                        let maxNum = adcreativeTemplateId === 1708 || adcreativeTemplateId === 1707 ? item.pupState.xd_show ? 10 : item.restriction.textRestriction.maxLength : item.restriction.textRestriction.maxLength
+                                        return <div key={'description' + item.fieldType}>
+                                            <Form.Item {...field} label={<strong>{item.description}</strong>} name={[field.name, item.name]} rules={[{ required: true, pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${maxNum}}`)), message: '请输入正确的' + item.description }]}>
+                                                <TextAideInput placeholder={'请输入' + item.description} style={{ width: 450 }} maxTextLength={maxNum} />
+                                            </Form.Item>
+                                        </div>
+                                    }
+                                    return null
+                                })}
+                            </Collapse.Panel>))}
+                        </Collapse>
+                        <Form.Item>
+                            <Button type="link" onClick={() => add()} icon={<PlusOutlined />} style={{ padding: 0 }}>
+                                新增文案组
+                            </Button>
+                        </Form.Item>
+                    </>)}
+                </Form.List>
+            </Form>
+        </Modal>}
+    </>
+}
+
+
+export default React.memo(Copywriting)

+ 150 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/headNickJump.tsx

@@ -0,0 +1,150 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import { addSysProfileApi, delSysProfileApi, editSysProfileApi, getSysProfileApi } from "@/services/launchAdq/global"
+import { PlusOutlined } from "@ant-design/icons"
+import { Button, Divider, Form, Input, message, Modal, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { UploadImage } from "./brandImage"
+import { profileColumns } from "./tableConfig"
+
+interface Props {
+    onChange?: (data: any) => void,
+    value?: any
+}
+
+/**
+ * 头像及昵称跳转页
+ * @returns 
+ */
+const HeadNickJump: React.FC<Props> = (props) => {
+
+    /****************************/
+    const { onChange, value } = props
+
+    const [visible, setVisible] = useState<boolean>(false)
+    const [addVisible, setAddVisible] = useState<boolean>(false)
+    const [form] = Form.useForm()
+    const [initialValues, setInitialValues] = useState<any>({})
+
+    const getSysProfile = useAjax(() => getSysProfileApi())
+    const addSysProfile = useAjax((params) => addSysProfileApi(params))
+    const editSysProfile = useAjax((params) => editSysProfileApi(params))
+    const delSysProfile = useAjax((params) => delSysProfileApi(params))
+    /****************************/
+
+    // 获取列表
+    useEffect(() => {
+        getSysProfile.run()
+    }, [])
+
+
+    // 新增修改
+    const handleOk = async () => {
+        form.submit()
+        let data = await form.validateFields()
+        if (Object.keys(initialValues).length > 0) { // 修改
+            editSysProfile.run({ ...data, sysProfileId: initialValues.id }).then(res => {
+                if (res) {
+                    message.success('修改成功')
+                    setAddVisible(false)
+                    getSysProfile.refresh()
+                }
+            })
+        } else { // 新增
+            addSysProfile.run(data).then(res => {
+                if (res) {
+                    message.success('新增成功')
+                    setAddVisible(false)
+                    getSysProfile.refresh()
+                }
+            })
+        }
+        setInitialValues({})
+        form.resetFields()
+    }
+
+    /** 删除 */
+    const del = (id: number) => {
+        delSysProfile.run({ sysProfileId: id }).then(res => {
+            if (res) {
+                message.success('删除成功')
+                getSysProfile.refresh()
+            }
+        })
+    }
+
+    /** 修改 */
+    const edit = (data: any) => {
+        setInitialValues(data)
+        setAddVisible(true)
+    }
+
+    return <div>
+        <Select
+            showSearch
+            placeholder="请选择一个品牌跳转页,与广告创意一起展示"
+            optionFilterProp="children"
+            style={{ width: 400 }}
+            onChange={(e) => { onChange && onChange(e) }}
+            allowClear
+            value={value}
+            filterOption={(input, option) => {
+                return (option!.value as unknown as string).toLowerCase().includes(input.toLowerCase())
+            }}
+            dropdownRender={menu => <>
+                {menu}
+                <Divider style={{ margin: '8px 0' }} />
+                <div>
+                    <Button type="link" onClick={() => { setAddVisible(true); setInitialValues({}) }}>新增</Button>
+                    <Button type="link" onClick={() => setVisible(true)}>前往管理</Button>
+                </div>
+            </>}
+        >
+            {
+                getSysProfile?.data?.map((item: any) => {
+                    return <Select.Option value={item.profileName + '_' + item.headImageUrl + '_' + item.description} key={item.id}>
+                        <Space>
+                            <img src={item.headImageUrl} style={{ width: 20 }} />
+                            <span>{item.profileName}</span>
+                        </Space>
+                    </Select.Option>
+                })
+            }
+        </Select>
+
+        {visible && <Modal title="头像及昵称跳转页" width={1000} visible={visible} footer={null} onCancel={() => setVisible(false)}>
+            <Space direction='vertical' style={{ width: '100%' }}>
+                <Button type="primary" icon={<PlusOutlined />} onClick={() => { setAddVisible(true); setInitialValues({}) }}>上传品牌形象</Button>
+                <Tables
+                    columns={profileColumns(del, edit)}
+                    dataSource={getSysProfile?.data}
+                    size="small"
+                    loading={getSysProfile?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                />
+            </Space>
+        </Modal>}
+        {addVisible && <Modal title={`${Object.keys(initialValues).length > 0 ? '修改' : '上传'}头像及昵称跳转页`} visible={addVisible} confirmLoading={addSysProfile.loading || editSysProfile.loading} onOk={handleOk} onCancel={() => setAddVisible(false)}>
+            <Form
+                name="basic"
+                form={form}
+                layout='vertical'
+                autoComplete="off"
+                initialValues={{ ...initialValues }}
+            >
+                <Form.Item label={<strong>头像</strong>} name="headImageUrl" rules={[{ required: true, message: '请选择头像!' }]}>
+                    <UploadImage />
+                </Form.Item>
+                <Form.Item label={<strong>名称</strong>} name="profileName" rules={[{ required: true, message: '请输入名称!' }]}>
+                    <Input placeholder="请输入名称" maxLength={12} />
+                </Form.Item>
+                <Form.Item label={<strong>详细描述</strong>} name="description" rules={[{ required: true, message: '请输入详细描述!' }]}>
+                    <Input.TextArea placeholder="请输入详细描述" maxLength={120} />
+                </Form.Item>
+            </Form>
+        </Modal>}
+    </div>
+}
+
+export default React.memo(HeadNickJump)

+ 117 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.less

@@ -0,0 +1,117 @@
+.adcreative_template {
+    width: 100%;
+    overflow-y: auto;
+    display: flex;
+    height: 173px;
+
+    >label {
+        height: 100%;
+        margin-right: 15px;
+    }
+}
+
+.videoImgs {
+    width: 100%;
+    overflow-y: auto;
+    display: flex;
+
+    img {
+        width: 100%;
+    }
+
+    label {
+        height: 100%;
+        padding: 0;
+        width: 32%;
+        margin-right: 1%;
+    }
+}
+
+.adcreative_template_item {
+    width: 150px;
+    height: 160px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    flex-flow: column;
+}
+
+.video {}
+
+.box {
+    width: 60%;
+    height: 200px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #f5f7fa;
+    flex-direction: column;
+    color: rgba(0, 0, 0, .3);
+    border-radius: 5px;
+
+    /* TWEENER - IE 10 */
+    >p {
+        display: flex;
+        align-items: center;
+        flex-flow: column;
+        font-size: 10px;
+        cursor: pointer;
+        max-height: 150px;
+        margin-bottom: 0;
+        max-width: 100%;
+
+        img {
+            height: 100%;
+        }
+
+        video {
+            height: 100%;
+        }
+    }
+}
+
+.image_list {
+    flex-flow: row wrap;
+    background-color: transparent;
+    height: auto;
+    justify-content: flex-start;
+
+    >p {
+        width: 150px;
+        background-color: #f5f7fa;
+        height: 150px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        border: 1px solid #e6e8ed;
+        margin: 0;
+    }
+}
+
+.imageMater {
+    width: 300px;
+    height: 160px;
+}
+
+.crt {
+    display: inline-flex;
+    align-items: center;
+    width: auto;
+    margin-left: 8px;
+    padding: 1px 4px;
+    height: 16px;
+    border-radius: 3px;
+    font-size: 12px;
+    color: #fff;
+    border: 1px solid #296bef;
+    background-color: #296bef;
+    line-height: normal;
+}
+
+.space {
+    width: 100%;
+
+    .clear {
+        width: 20px;
+    }
+}

+ 894 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/index.tsx

@@ -0,0 +1,894 @@
+import React, { useCallback, useEffect, useMemo, useState } from 'react'
+import { Modal, Form, Input, Divider, Select, Radio, Switch, Spin, List, Space, Button, message, Image, Empty } from 'antd'
+import styles from './index.less'
+import { useAjax } from '@/Hook/useAjax'
+import { getText, get_adcreative_template, get_adcreative_template_list, get_tools_video_capture } from '@/services/launchAdq/global'
+import { AdcreativeTemplate, AdcreativeTemplateList } from '@/services/launchAdq'
+import { mySet } from '@/utils/arrFn'
+import SelectCloud from '@/pages/launchSystemNew/components/selectCloud'
+import { useModel } from 'umi'
+import { ModalConfig } from '../../ad';
+import { outAdcreativeTemplateIdFun } from '../../../localAd/adenum'
+import { CreateAdProps } from '@/services/launchAdq/createAd'
+import { creativeConfig, overrideCanvasHeadOptionEnum } from './config'
+import BrandImage from './brandImage'
+import HeadNickJump from './headNickJump'
+import moment from 'moment'
+interface Props {
+    queryForm: Partial<CreateAdProps>,
+    title?: string,
+    visible: boolean,
+    PupFn: (arg: ModalConfig) => void,
+    callback: (params: any, material: { label: string, name: string, restriction: any }[], textData: any[]) => void,
+    confirmLoading?: boolean,
+    type?: 'add' | 'look' | 'edit',//新增,查看,编辑
+    dataInfo?: any
+}
+
+/**创意组模板*/
+function CreativePup(props: Props) {
+
+    /***************************************/
+    let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm } = props
+    const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
+    let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
+    let { promotedObjectType, sysAdgroup } = queryForm
+    let { siteSet } = sysAdgroup
+    let arg = type === 'look' ? { footer: null } : {}
+    // 请求
+    const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
+    const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
+    const getTextLsit = useAjax((params) => getText(params))
+    const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
+    // 变量
+    const [adcreative_template, set_adcreative_template] = useState<AdcreativeTemplate>()
+    const [adcreative_template_list, set_adcreative_template_list] = useState<AdcreativeTemplateList[]>([])
+    const [selectImgVisible, set_selectImgVisible] = useState(false)
+    const [selectVideoVisible, set_selectVideoVisible] = useState(false)
+    const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
+    const [endPageDescShow, setendPageDescnshow] = useState(false)
+    const [materialData, setMaterialData] = useState<{ label: string, name: string, restriction: any }[]>([])
+    const [textData, setTextData] = useState<any[]>([])
+    const [isShowSc, set_isShowSc] = useState(false)//是否展示素材选项
+    const [infoSet, set_infoSet] = useState(false)//回填设置已完成
+    const [isShowXd, setIsShowXd] = useState(false)
+    const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
+        activeUrl: string,//选中的视频封面图地址
+        preview: boolean,//是否开启图片点击预览
+        urlList: any[],//生成的视频封面列表
+    }>({
+        activeUrl: '',
+        preview: false,
+        urlList: [
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
+        ]
+    })
+    const [form] = Form.useForm();
+    const [pupState, setPupState] = useState({
+        kp_show: false,
+        xd_show: false,
+        sj_show: false,
+        bq_show: false,
+        sp_show: false
+    })
+    const [imgMaterialConfig, setImgMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        max: number
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        max: 1,//素材数量
+    })//图片素材配置
+    const [videoMaterialConfig, setVideoMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        max: number
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        max: 1,//素材数量
+    })//图片素材配置
+    const [conversionList, setConversionList] = useState<any>(null)
+    let pageType = Form.useWatch('pageType', form)
+    let adcreativeTemplateId = Form.useWatch('adcreativeTemplateId', form)
+    let actionBtn = Form.useWatch('actionBtn', form)
+    // let siteSet = Form.useWatch('siteSet', form)
+    let overrideCanvasHeadOption = Form.useWatch('overrideCanvasHeadOption', form)
+    let adcreativeElementsType = Form.useWatch('adcreativeElementsType', form)
+    let dataShow = Form.useWatch('dataShow', form)
+    let conversionDataType = Form.useWatch('conversionDataType', form)
+    let titles = Form.useWatch('title', form)
+    let description = Form.useWatch('description', form)
+    let videoOver = Form.useWatch('videoOver', form)
+    let endPageDesc = Form.useWatch('endPageDesc', form)
+    let linkPageType = Form.useWatch('linkPageType', form)
+    /***************************************/
+
+    // 确定事件
+    const handleOk = useCallback(() => {
+        form.validateFields().then(values => {
+            console.log('values=>1', values)
+            let newValues = JSON.parse(JSON.stringify(values))
+            for (let key in newValues) {
+                switch (key) {
+                    case 'image'://图素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            imageUrl: imgMaterialConfig?.list[0]?.url,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'video'://视频素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            videoUrl: videoMaterialConfig?.list[0]?.url,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'image_list'://图素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            imageUrlList: imgMaterialConfig.list?.map(item => item.url),
+                            description: newValues.description,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'short_video1'://视频素材
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements,
+                            shortVideoStruct: {
+                                shortVideo1Url: videoMaterialConfig?.list[0]?.url
+                            },
+                            description: newValues.description,
+                        }
+                        delete newValues[key]
+                        break;
+                    case 'description'://文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, description: newValues.description }
+                        break;
+                    case 'title'://文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, title: newValues.title }
+                        break;
+                    case 'endPageType'://视频结束l类型
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageType: newValues.endPageType } }
+                        delete newValues[key]
+                        break;
+                    case 'endPageDesc'://视频结束文案
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageDesc: newValues.endPageDesc } }
+                        delete newValues[key]
+                        break;
+                    case 'buttonText'://特殊行动按钮
+                        newValues.adcreativeElements = { ...newValues.adcreativeElements, buttonText: newValues.buttonText }
+                        delete newValues[key]
+                        break;
+                    case 'brand'://品牌形象
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements, brand: {
+                                brandName: newValues.brand.split('_')[0],
+                                brandImgUrl: newValues.brand.split('_')[1]
+                            }
+                        }
+                        break;
+                    case 'profile':
+                        newValues.adcreativeElements = {
+                            ...newValues.adcreativeElements, brand: {
+                                brandName: newValues.profile.split('_')[0],
+                                brandImgUrl: newValues.profile.split('_')[1]
+                            }
+                        }
+                        newValues.profile = {
+                            headImageUrl: newValues.profile.split('_')[1],
+                            profileName: newValues.profile.split('_')[0],
+                            description: newValues.profile.split('_')[2]
+                        }
+                        break
+                    case 'pageUrl'://跳转落地页
+                        newValues.linkPageSpec = {
+                            ...newValues.linkPageSpec,
+                            pageUrl: newValues.pageUrl
+                        }
+                        delete newValues.pageUrl
+                        break;
+                    case 'miniProgramId':
+                        newValues.linkPageSpec = {
+                            ...newValues.linkPageSpec,
+                            miniProgramSpec: {
+                                miniProgramId: newValues.miniProgramId,
+                                miniProgramPath: newValues.miniProgramPath
+                            }
+                        }
+                        delete newValues.miniProgramId
+                        delete newValues.miniProgramPath
+                        break;
+                }
+            }
+            if (!newValues.adcreativeElements) {
+                newValues.adcreativeElements = {}
+            }
+            //假如不存在promotedObjectType
+            if (!newValues?.promotedObjectType) {
+                newValues['promotedObjectType'] = queryForm.promotedObjectType
+            }
+            // 假如不存在siteSet
+            if (!newValues?.siteSet) {
+                newValues['siteSet'] = queryForm.sysAdgroup.siteSet
+            }
+            delete newValues.description //删除外层文案
+            delete newValues.title //删除外层文案
+            delete newValues.adcreativeElementsType //删除创意形式
+            delete newValues.dataShow //删除数据开关
+            delete newValues.actionBtn //删除行动开关
+            delete newValues.brand //品牌形象
+            // 假如使用了落地页顶部素材替换外部素材
+            if (newValues.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                console.log(adcreative_template?.adcreativeElements)
+                adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').forEach(item => {
+                    switch (item.name) {
+                        case 'image'://图素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                imageUrl: '',
+                            }
+                            break;
+                        case 'video'://视频素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                videoUrl: '',
+                            }
+                            break;
+                        case 'image_list'://图素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                imageUrlList: [],
+                            }
+                            break;
+                        case 'short_video1'://视频素材
+                            newValues.adcreativeElements = {
+                                ...newValues.adcreativeElements,
+                                shortVideoStruct: {
+                                    shortVideo1Url: ''
+                                },
+                            }
+                            break;
+                    }
+                })
+            }
+            console.log('newValues=>2', newValues)
+            newValues['isTemplate'] = template_checked
+            // // 开启存为模板开关执行
+            callback(newValues, materialData, textData)
+        })
+    }, [form, imgMaterialConfig, videoMaterialConfig, materialData, textData, queryForm, template_checked, adcreative_template, isShowSc])
+    // 获取创意形式列表
+    useEffect(() => {
+        if (siteSet?.length > 0 && promotedObjectType) {
+            getAdcreativeTemplateList.run({
+                siteSet,
+                promotedObjectType,
+                campaignType: 'CAMPAIGN_TYPE_NORMAL',
+            }).then(res => {
+                let newArr: any = []
+                let newData: any[] = []
+                // 过滤掉相同的和即将下线的
+                if (!res) {
+                    return
+                }
+                // 
+                Object.values(res)?.forEach((arr: any) => {
+                    newData.push(arr)
+                    Array.isArray(arr) && arr?.forEach((item: any) => {
+                        if (newArr.length > 0) {//假如已存在ID,需要过滤相同
+                            if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {//不重复的添加
+                                newArr.push(item)
+                            }
+                        } else {//不存在ID直接过滤掉即将下线的
+                            if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
+                                newArr.push(item)
+                            }
+                        }
+                    })
+                })
+                /*****暂时排除激励和banner有问题******/
+                if (siteSet.some((i: string) => i === 'SITE_SET_MOMENTS')) {
+                    newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 910 && item.adcreativeTemplateId !== 925)
+                }
+                /*****暂时排除出框形态 视频合约广告******/
+                if (siteSet.some((i: string) => i === 'SITE_SET_WECHAT')) {
+                    newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 1945)
+                }
+                let newArr1: any[] = []
+                let newArr2: any[] = []
+                newArr?.forEach((arr: { adcreativeTemplateId: any, isGeneral?: boolean }) => {
+                    if (newData.every((item: { adcreativeTemplateId: any }[]) => item.find(i => i.adcreativeTemplateId === arr.adcreativeTemplateId))) {
+                        newArr1.push({ ...arr, isGeneral: true })
+                    } else {
+                        newArr2.push(arr)
+                    }
+                })
+                set_adcreative_template_list([...newArr1, ...newArr2])
+            })
+        }
+    }, [siteSet, promotedObjectType])
+    // 获取创意形式详情
+    const getTemplate = (id: any, ok?: any) => {
+        // CAMPAIGN_TYPE_NORMAL
+        if (siteSet?.length > 0 && promotedObjectType && id) {
+            if (id) {
+                getAdcreativeTemplate.run({
+                    siteSet,
+                    promotedObjectType,
+                    adcreativeTemplateId: id
+                }).then(res => {
+                    if (res?.length > 0) {
+                        form.setFieldsValue({ adcreativeName: res[0]?.adcreativeTemplateAppellation + '_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId })
+                        set_adcreative_template(res[0])
+                        if (siteSet?.some((name: string) => name === 'SITE_SET_MOMENTS')) {
+                            let id = res[0].adcreativeTemplateId
+                            set_isShowSc(!!creativeConfig[id])//判定当前创意是否需要展示替换素材选项
+                            if (creativeConfig[id] && !ok) {//假如不等于回填元素的ID
+                                let overrideCanvasHeadOption = creativeConfig[id].overrideCanvasHeadOption
+                                form.setFieldsValue({ overrideCanvasHeadOption: overrideCanvasHeadOption?.includes('OPTION_CREATIVE_OVERRIDE_CANVAS') ? 'OPTION_CREATIVE_OVERRIDE_CANVAS' : overrideCanvasHeadOption[0] })
+                            }
+                        }
+                        templateChange(res[0], ok)
+                        // 处理素材
+                        setMaterialData(res[0]?.adcreativeElements?.filter((item: any) => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').map((item: any) => {
+                            return {
+                                label: item.description === '图片' && res[0]?.adcreativeElements?.some((item: any) => item.name === 'video') ? '视频封面图' : item.description,
+                                name: item.name,
+                                restriction: item.restriction,
+                                arrayProperty: item?.arrayProperty
+                            }
+                        }))
+                        // 处理文案
+                        setTextData(res[0]?.adcreativeElements?.filter((item: any) => item.name === 'title' || (item.required && item.name === 'description')).map((item: any) => ({ ...item, pupState })))
+                    }
+                })
+            }
+        }
+    }
+    // 获取对应落地页按钮
+    const pageTypeList = useMemo(() => {
+        if (adcreativeTemplateId) {
+            let arr: any = adcreative_template?.landingPageConfig?.supportPageTypeList
+            return arr
+        }
+        return null
+
+    }, [adcreativeTemplateId, adcreative_template])
+    // 获取对应行动按钮数据
+    const linkNameList = useMemo(() => {
+        if (pageType) {
+            let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
+            return arr
+        }
+        return null
+    }, [pageType, pageTypeList])
+    // 跳转落地页
+    const linkPageList = useMemo(() => {
+        if (pageType) {
+            let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
+            return arr
+        }
+        return null
+    }, [pageType, pageTypeList])
+    const typeChange = useCallback((adcreativeElementsType) => {
+        if (adcreativeElementsType && adcreative_template_list?.length > 0) {
+            let adcreativeTemplateIdArr = adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)
+            console.log('typeChange====>', adcreativeTemplateIdArr[0].adcreativeTemplateId)
+            getTemplate(adcreativeTemplateIdArr[0].adcreativeTemplateId)
+            form.setFieldsValue({ adcreativeTemplateId: adcreativeTemplateIdArr[0].adcreativeTemplateId })
+
+        }
+    }, [adcreative_template_list])
+
+    //每次选中创意设置该展示的界面
+    const templateChange = (adcreative_template: any, ok?: any) => {
+        let states = {
+            kp_show: false,
+            xd_show: true,
+            sj_show: false,
+            bq_show: false,
+            sp_show: false
+        }
+        let values: any = { pageType: 'PAGE_TYPE_CANVAS_WECHAT', }
+        if (adcreative_template) {
+            let pageList = adcreative_template?.landingPageConfig?.supportPageTypeList?.filter((i: { description: string | string[] }) => i.description.includes('微信原生推广页'))//当前版本只获取微信原生页,后期改进
+            let pageType = pageList?.length ? pageList[0]?.pageType : null
+            //数据展示组件
+            if (adcreative_template.adcreativeAttributes.some((item: { name: string }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
+                let arr = adcreative_template.adcreativeAttributes?.filter((item: { name: string; }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')
+                let newObj: any = {}
+                arr.forEach((item: { propertyDetail: { enumDetail: { enumeration: any[] } }; name: string | number }) => {
+                    let arr: any[] = mySet(item.propertyDetail.enumDetail.enumeration)
+                    newObj[item.name] = arr
+                })
+                setConversionList(newObj)
+                states = { ...states, sj_show: true }
+                if (newObj.conversion_data_type) {
+                    values = { ...values, conversionDataType: newObj.conversion_data_type[0].value }
+                }
+                if (newObj.conversion_target_type) {
+                    values = { ...values, conversionTargetType: newObj.conversion_target_type[0].value }
+                }
+            }
+
+            //行动按钮组件存在
+            if (states.xd_show) {
+                let supportLinkNameTypeData = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType
+                let linkNameList = supportLinkNameTypeData?.list
+                let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
+                if (linkNameList) { // && !linkPageType
+                    if (!ok) {
+                        let linkNameType = linkNameList[0]?.linkNameType
+                        let linkPageType = linkPageList?.some((i: { linkPageType: string }) => i.linkPageType === "LINK_PAGE_TYPE_CANVAS_WECHAT") ? "LINK_PAGE_TYPE_CANVAS_WECHAT" : linkPageList[0]?.linkPageType
+                        values = { ...values, linkNameType, linkPageType, actionBtn: true }
+                    }
+                } else {
+                    states = { ...states, xd_show: false }
+                }
+                if (supportLinkNameTypeData?.required) {
+                    states = { ...states, xd_show: true }
+                    values = { ...values, actionBtn: true }
+                    setIsShowXd(true)
+                } else {
+                    setIsShowXd(false)
+                }
+            }
+            // 特殊行动按钮
+            if (adcreative_template.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text') && !ok) {
+                values = { ...values, buttonText: adcreative_template?.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text')?.enumProperty?.enumeration[0].value }
+            }
+            // 视频结束页 end_page
+            if (adcreative_template.adcreativeElements.some((item: { name: string }) => item.name === 'end_page')) {
+                // let endPageType =adcreative_template?.adcreativeElements?.filter(item=>item.name === 'end_page_type')[0]?.enumProperty?.enumeration
+                if (!ok) {
+                    values = { ...values, endPageType: 'END_PAGE_AVATAR_NICKNAME_HIGHLIGHT' }
+                }
+                states = { ...states, sp_show: true }
+            }
+            setPupState(states)
+            form.setFieldsValue(values)
+        }
+    }
+    // 版位改变清空数据
+    useEffect(() => {
+        if (imgMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== imgMaterialConfig.adcreativeTemplateId) {
+            setImgMaterialConfig({ ...imgMaterialConfig, adcreativeTemplateId: undefined, list: [] })
+        }
+        if (videoMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== videoMaterialConfig.adcreativeTemplateId) {
+            setVideoMaterialConfig({ ...videoMaterialConfig, adcreativeTemplateId: undefined, list: [] })
+        }
+    }, [adcreativeTemplateId, imgMaterialConfig, videoMaterialConfig])
+    // 文案助手
+    const textList = useCallback((arg: { maxTextLength: number, keyword?: string }) => {
+        let { maxTextLength, keyword } = arg
+        getTextLsit.run({ keyword: keyword || titles || description, maxTextLength })
+    }, [titles, description])
+    // 监听点击取消文案助手弹窗
+    useEffect(() => {
+        let modal = document.querySelector('.myModal')
+        let onBiurdescription = (e: any) => {
+            let p = document.querySelector('.my_endPageDesc')
+            if (!p?.contains(e.target)) {
+                setendPageDescnshow(false)
+            }
+        }
+        modal?.addEventListener('click', onBiurdescription)
+        return () => {
+            modal?.removeEventListener('click', onBiurdescription)
+        }
+    }, [])
+    // 数据回填
+    useEffect(() => {
+        if (!infoSet && dataInfo && adcreative_template_list?.length > 0) {
+            let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, profile, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
+            let { description, imageUrl, title, videoUrl, imageUrlList, endPage, shortVideoStruct, brand, buttonText } = adcreativeElements
+            let obj: any = {
+                adcreativeName,
+                siteSet,
+                promotedObjectType,
+                adcreativeTemplateId,
+            }
+            getTemplate(adcreativeTemplateId, true)
+            console.log(2222, dataInfo)
+            if ([720, 721, 618, 1708, 722, 1529].some(n => n === adcreativeTemplateId)) {
+                obj = { ...obj, adcreativeElementsType: '视频' }
+            } else {
+                obj = { ...obj, adcreativeElementsType: '图片' }
+            }
+            if (conversionDataType) {
+                obj = { ...obj, conversionDataType, dataShow: true }
+            }
+            if (conversionTargetType) {
+                obj = { ...obj, conversionTargetType, dataShow: true }
+            }
+            if (linkNameType) {
+                obj = { ...obj, linkNameType, actionBtn: true }
+            }
+            if (linkPageType) {
+                obj = { ...obj, linkPageType, actionBtn: true }
+            }
+            if (pageType) {
+                obj = { ...obj, pageType }
+            }
+            if (description) {
+                obj = { ...obj, description }
+            }
+            if (title) {
+                obj = { ...obj, title }
+            }
+            if (endPage) {
+                obj = { ...obj, videoOver: true, ...endPage }
+            }
+            if (overrideCanvasHeadOption) {
+                obj = { ...obj, overrideCanvasHeadOption }
+            }
+            if (linkPageSpec?.pageUrl) {
+                obj = { ...obj, pageUrl: linkPageSpec?.pageUrl }
+            }
+            if (linkPageSpec?.miniProgramSpec && linkPageSpec?.miniProgramSpec?.miniProgramPath) {
+                obj = { ...obj, miniProgramPath: linkPageSpec?.miniProgramSpec?.miniProgramPath, miniProgramId: linkPageSpec?.miniProgramSpec?.miniProgramId }
+            }
+            if (brand && brand.brandImgUrl && brand.brandName) {
+                obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
+            }
+            if (profile && profile.headImageUrl && profile.profileName && profile.description) {
+                obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description }
+            }
+            if (buttonText) {
+                obj = { ...obj, buttonText }
+            }
+            if (videoUrl) {
+                setVideoMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: videoUrl }],
+                    max: 1,
+                    type: 'video',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, video: videoUrl }
+            }
+            if (imageUrl) {
+                setImgMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: imageUrl }],
+                    max: 1,
+                    type: 'image',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, image: imageUrl }
+            }
+            if (imageUrlList) {
+                setImgMaterialConfig({
+                    cloudSize: [],
+                    list: imageUrlList?.map((url: any) => ({ url })),
+                    max: imageUrlList.length,
+                    type: 'image_list',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, image_list: imageUrlList }
+            }
+            if (shortVideoStruct) {
+                setVideoMaterialConfig({
+                    cloudSize: [],
+                    list: [{ url: shortVideoStruct.shortVideo1Url }],
+                    max: 1,
+                    type: 'short_video1',
+                    adcreativeTemplateId
+                })
+                obj = { ...obj, short_video1: shortVideoStruct.shortVideo1Url }
+            }
+            console.log('数据回填====>', obj)
+            form.setFieldsValue(obj)
+            set_infoSet(true)
+        }
+        // 不是数据回填首次打开界面选中视频
+        if (!infoSet && !dataInfo && adcreative_template_list?.length > 0) {
+            typeChange('视频')
+            set_infoSet(true)
+        }
+    }, [dataInfo, adcreative_template_list, adcreative_template, infoSet])
+    return <Modal
+        visible={visible}
+        title={type === 'add' ? '新建创意' : type === 'look' ? '创意详情' : '编辑创意'}
+        onCancel={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}
+        // onOk={handleOk}
+        width={1200}
+        confirmLoading={confirmLoading}
+        footer={<Space>
+            <Button onClick={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}>取消</Button>
+            <Button type='primary' onClick={handleOk}>确定</Button>
+        </Space>}
+        className='myModal'
+        {...arg}
+    >
+        <Form
+            form={form}
+            labelCol={{ span: 5 }}
+            labelWrap={true}
+            className='ad_form_style'
+            initialValues={
+                {
+                    adcreativeElementsType: '视频',
+                }
+            }
+        >
+            {/* ============================================================创意形式============================================================= */}
+            <Divider orientation='center'>创意形式</Divider>
+            {/* ============================================================创意形式============================================================= */}
+            <Form.Item label={<strong>创意形式</strong>} name='adcreativeElementsType'>
+                <Radio.Group onChange={(e) => {
+                    let value = e.target.value
+                    typeChange(value)
+                }}>
+                    <Radio.Button value="视频">视频</Radio.Button>
+                    <Radio.Button value="图片">图片</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+
+            {
+                getAdcreativeTemplateList?.loading ? <Spin tip="Loading..." style={{ width: '100%' }}></Spin> :
+                    <>
+                        <Form.Item style={{ marginLeft: 177 }} name='adcreativeTemplateId'>
+                            <Radio.Group className={styles.adcreative_template} onChange={(e) => {
+                                let id = e.target.value
+                                getTemplate(id)
+                            }}>
+                                {adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType && item.supportBidModeList.includes(queryForm?.sysAdgroup?.bidMode))?.map((item: any) => {
+                                    return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
+                                        <div className={styles.adcreative_template_item}>
+                                            {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
+                                            <img src={item.adcreativeSampleImage} />
+                                            <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
+                                            <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
+                                        </div>
+                                    </Radio.Button>
+                                })}
+                            </Radio.Group>
+                        </Form.Item>
+                        {/* ============================================================创意内容============================================================= */}
+                        <Divider orientation='center'>创意内容</Divider>
+                        {/* =============================================================头像及昵称跳转页===================================================================== */}
+                        {queryForm.promotedObjectType === 'PROMOTED_OBJECT_TYPE_LEAD_AD' ? adcreative_template?.adcreativeAttributes?.find(item => item.name === 'profile_id') ? <Form.Item label={<strong>头像及昵称跳转页</strong>} name='profile' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
+                            <HeadNickJump />
+                        </Form.Item> : <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
+                            <BrandImage />
+                        </Form.Item> : null}
+                        {/* ============================================================落地页============================================================= */}
+                        {adcreativeTemplateId ? <Form.Item label={<strong>落地页</strong>} name='pageType' >
+                            <Radio.Group>
+                                {pageTypeList?.map((item: any) => {
+                                    return <Radio.Button value={item.pageType} key={item.pageType} disabled={!item.description.includes('微信原生推广页')}>{item.description.includes('微信原生推广页') ? '微信原生推广页' : item.description}</Radio.Button>
+                                })}
+                            </Radio.Group>
+                        </Form.Item> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+                            <Empty description="请先选择创意形式" />
+                        </div>}
+                        {
+                            pageType === 'PAGE_TYPE_CANVAS_WECHAT' && isShowSc && <Form.Item label={<strong>素材选项</strong>} name='overrideCanvasHeadOption'>
+                                <Radio.Group disabled>
+                                    {adcreativeTemplateId && creativeConfig[adcreativeTemplateId]?.overrideCanvasHeadOption?.map((item: string | number) => {
+                                        return <Radio value={item} key={item}>{overrideCanvasHeadOptionEnum[item]}</Radio>
+                                    })}
+                                </Radio.Group>
+                            </Form.Item>
+                        }
+                        {/* ============================================================普通行动按钮============================================================= */}
+                        {
+                            pupState.xd_show && <Form.Item hidden={isShowXd} label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
+                                <Switch checkedChildren="开启" unCheckedChildren="关闭" disabled={isShowXd}/>
+                            </Form.Item>
+                        }
+                        {
+                            actionBtn && <>
+                                <Form.Item name='linkNameType' label={<strong>按钮文案</strong>}>
+                                    <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
+                                        (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                                    } allowClear>
+                                        {linkNameList?.map((item: any) => {
+                                            return <Select.Option value={item.linkNameType} key={item.linkNameType}>{item.description}</Select.Option>
+                                        })}
+                                    </Select>
+                                </Form.Item>
+                                <Form.Item label={<strong>跳转落地页</strong>}>
+                                    <Form.Item name='linkPageType' noStyle>
+                                        <Radio.Group style={{ display: 'flex' }}>
+                                            {linkPageList?.map((item: { linkPageType: string; description: string; }, index: number) => {
+                                                return <Radio.Button value={item.linkPageType} key={item.linkPageType} >{item.description}</Radio.Button>
+                                            })}
+                                        </Radio.Group>
+                                    </Form.Item>
+                                    {/* 自定义落地页地址 */}
+                                    {linkPageType === "LINK_PAGE_TYPE_DEFAULT" && <Form.Item name='pageUrl' rules={[{ required: true, message: '请输入自定义落地页地址' }]} style={{ marginTop: 10, marginBottom: 0 }}>
+                                        <Input placeholder='请输入自定义落地页地址' style={{ width: 300 }} />
+                                    </Form.Item>}
+                                    {/* 小程序 */}
+                                    {linkPageType === "LINK_PAGE_TYPE_MINI_PROGRAM_WECHAT" && <Form.Item noStyle >
+                                        <Form.Item rules={[{ required: true, message: '请输入小程序原始ID' }]} name='miniProgramId' style={{ marginTop: 10, marginBottom: 0 }} >
+                                            <Input placeholder='请输入小程序原始ID' style={{ width: 300 }} />
+                                        </Form.Item>
+                                        <Form.Item rules={[{ required: true, message: '请输入小程序链接' }]} name='miniProgramPath' style={{ marginTop: 10, marginBottom: 0 }}>
+                                            <Input placeholder='请输入小程序链接' style={{ width: 300 }} />
+                                        </Form.Item>
+                                    </Form.Item>}
+                                </Form.Item>
+                                {/* 落地页 */}
+
+                            </>
+                        }
+                        {/* ============================================================特殊行动按钮============================================================= */}
+                        {
+                            !pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
+                                <Form.Item valuePropName="checked" noStyle >
+                                    <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={true} disabled defaultChecked={true} />
+                                </Form.Item>
+                            </Form.Item>
+                        }
+                        {
+                            !pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
+                                <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
+                                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                                } allowClear>
+                                    {adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text')?.enumProperty?.enumeration?.map((item: any) => {
+                                        return <Select.Option value={item.value} key={item.value}>{item.value}</Select.Option>
+                                    })}
+                                </Select>
+                            </Form.Item>
+                        }
+                        {/* ============================================================数据展示============================================================= */}
+                        {pupState.sj_show && <Form.Item label={<strong>数据展示</strong>} name='dataShow' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {
+                            dataShow && <>
+                                <Form.Item name='conversionDataType' label={<strong>数据类型</strong>}>
+                                    <Radio.Group>
+                                        {conversionList?.conversion_data_type?.map((item: { value: string; description: string; }, index: number) => {
+                                            return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                        })}
+                                    </Radio.Group>
+                                </Form.Item>
+                                {conversionList?.conversion_target_type && conversionDataType === 'CONVERSION_DATA_ADMETRIC' && <Form.Item name='conversionTargetType' label={<strong>转化行为</strong>}>
+                                    <Radio.Group>
+                                        {conversionList?.conversion_target_type?.map((item: { value: string; description: string; }, index: number) => {
+                                            return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                        })}
+                                    </Radio.Group>
+                                </Form.Item>}
+                            </>
+                        }
+                        {/* ============================================================视频结束页============================================================= */}
+                        {pupState.sp_show && <Form.Item label={<strong>视频结束页</strong>} name='videoOver' valuePropName="checked">
+                            <Switch checkedChildren="开启" unCheckedChildren="关闭" />
+                        </Form.Item>}
+                        {
+                            videoOver && <>
+                                <Form.Item name='endPageType' label={<strong>视频结束页类型</strong>} >
+                                    <Radio.Group>
+                                        {
+                                            adcreative_template?.adcreativeElements?.filter(item => item.name === 'end_page_type')[0]?.enumProperty?.enumeration?.map((item) => {
+                                                return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
+                                            })
+                                        }
+                                    </Radio.Group>
+                                </Form.Item>
+                                <div className={'my_endPageDesc'} >
+                                    <Form.Item label={<strong>结束文案</strong>}>
+                                        <Form.Item name='endPageDesc' rules={[{ required: true, pattern: RegExp("^[^\\<\\>\\&'\\\"\\/\\x08\\x09\\x0A\\x0D\\\\]{1,12}$"), message: '请输入正确的结束页文案' }]} noStyle>
+                                            <Input
+                                                placeholder='请输入结束页文案'
+                                                style={{ width: 300 }}
+                                                onFocus={() => {
+                                                    setendPageDescnshow(true)
+                                                    textList({ maxTextLength: 12 })
+                                                }}
+                                                onChange={(e) => {
+                                                    let value = e.target.value
+                                                    textList({ maxTextLength: 12, keyword: value })
+                                                }}
+                                                allowClear
+                                            />
+                                        </Form.Item>
+                                        <span>{endPageDesc?.length || 0}/12</span>
+                                        {
+                                            endPageDescShow && <List
+                                                loading={getTextLsit?.loading}
+                                                size="small"
+                                                style={{ maxHeight: 300, maxWidth: 300, overflowX: 'auto' }}
+                                                bordered
+                                                dataSource={getTextLsit?.data?.returnTexts}
+                                                renderItem={(item: any) => <List.Item onClick={(e: any) => {
+                                                    form.setFieldsValue({ endPageDesc: item.text })
+                                                    setendPageDescnshow(false)
+                                                }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
+                                            />
+                                        }
+                                    </Form.Item>
+                                </div>
+                            </>
+                        }
+                    </>
+            }
+            {/* ============================================================基本信息============================================================= */}
+            <Divider orientation='center'>基本信息</Divider>
+            {/* ============================================================创意名称============================================================= */}
+            <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
+                <Input placeholder='创意名称' style={{ width: 300 }} />
+            </Form.Item>
+        </Form>
+        {/* 选择图片素材 */}
+        {
+            selectImgVisible && <SelectCloud
+                visible={selectImgVisible}
+                onClose={() => {
+                    set_selectImgVisible(false)
+                }}
+                sliderImgContent={imgMaterialConfig.list}
+                onChange={(content) => {
+                    if (content.length > 0) {
+                        form.setFieldsValue({ [imgMaterialConfig.type]: imgMaterialConfig.type })
+                    }
+                    setImgMaterialConfig({ ...imgMaterialConfig, list: content })
+                    set_selectImgVisible(false)
+                }} />
+        }
+        {/* 选择视频素材 */}
+        {
+            selectVideoVisible && <SelectCloud
+                visible={selectVideoVisible}
+                onClose={() => set_selectVideoVisible(false)}
+                sliderImgContent={videoMaterialConfig.list}
+                onChange={(content) => {
+                    if (content.length > 0) {
+                        form.setFieldsValue({ [videoMaterialConfig.type]: videoMaterialConfig.type })
+                    }
+                    setVideoMaterialConfig({ ...videoMaterialConfig, list: content })
+                    set_selectVideoVisible(false)
+                }} />
+        }
+        {/* 视频封面图弹窗 */}
+        {
+            videoImgsVisbile && <Modal
+                visible={videoImgsVisbile}
+                title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
+                onOk={() => {
+                    if (videoImgs.activeUrl) {
+                        setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
+                        set_videoImgsVisbile(false)
+                    } else {
+                        message.error('请选择图片,获取使用取消按钮关闭弹窗!')
+                    }
+                }}
+                onCancel={() => { set_videoImgsVisbile(false) }}
+                confirmLoading={getVideoCapture.loading}
+                width={600}
+            >
+
+                <Radio.Group className={styles.videoImgs} onChange={(e) => {
+                    let url = e.target.value
+                    set_videoImgs({ ...videoImgs, activeUrl: url })
+                }}>
+                    {
+                        videoImgs?.urlList?.map((item: any, index: number) => {
+                            return <Radio.Button value={item} key={index}>
+                                <Image src={item} preview={videoImgs.preview} />
+                            </Radio.Button>
+                        })
+                    }
+                </Radio.Group>
+            </Modal>
+        }
+    </Modal >
+}
+export default CreativePup

+ 434 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/material.tsx

@@ -0,0 +1,434 @@
+import { useAjax } from "@/Hook/useAjax"
+import SelectCloud from "@/pages/launchSystemNew/components/selectCloud"
+import { get_tools_video_capture } from "@/services/launchAdq/global"
+import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"
+import { Button, Form, message, Modal, Radio, Space, Switch, Image } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import styles from './index.less'
+
+interface Props {
+    value?: any[]
+    material?: { label: string, name: string, restriction: any, arrayProperty?: any }[],
+    sysAdcreative: any
+    onChange?: (data: any) => void
+}
+
+/**
+ * 选择素材组   
+ * @returns 
+ */
+const Material: React.FC<Props> = (props) => {
+
+    /**************************/
+    const { onChange, material = [], sysAdcreative, value } = props
+    const { adcreativeTemplateId } = sysAdcreative
+    const [visible, setVisible] = useState<boolean>(false)
+    const [selectVideoVisible, set_selectVideoVisible] = useState(false)
+    const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
+    const [videoUploads, setVideoUploads] = useState<any>()
+    const [imgUploads, setImgUploads] = useState<any>()
+    const { init } = useModel('useLaunchAdq.useBdMediaPup')
+    const [materialConfig, setMaterialConfig] = useState<{
+        adcreativeTemplateId?: number,
+        type: string,
+        cloudSize: { relation: string, width: number, height: number }[],
+        list: any[],
+        index: number,
+        max: number,
+        sliderImgContent: any
+    }>({
+        type: '',//类型
+        cloudSize: [],//素材搜索条件
+        list: [],//素材
+        index: 0, // 素材组下标
+        max: 1,//素材数量
+        sliderImgContent: undefined
+    })//图片素材配置
+    const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
+        activeUrl: string,//选中的视频封面图地址
+        preview: boolean,//是否开启图片点击预览
+        urlList: any[],//生成的视频封面列表
+    }>({
+        activeUrl: '',
+        preview: false,
+        urlList: [
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
+            'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
+        ]
+    })
+
+    const [form] = Form.useForm();
+    let materials = Form.useWatch('materials', form)
+
+    const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
+    /**************************/
+
+    // 回填
+    useEffect(() => {
+        if (visible) {
+            if (value && value?.length > 0) {
+                let data = value?.map((item: any) => {
+                    let data: any = {}
+                    for (const key in item) {
+                        switch (key) {
+                            case 'imageUrl'://图素材
+                                data.image = item[key]
+                                break;
+                            case 'videoUrl'://视频素材
+                                data.video = item[key]
+                                break;
+                            case 'imageUrlList'://图素材
+                                data.image_list = item[key]
+                                break;
+                            case 'shortVideo1Url'://视频素材
+                                data.short_video1 = item[key]
+                                break;
+                        }
+                    }
+                    return data
+                })
+                form.setFieldsValue({ materials: data })
+            } else {
+                form.setFieldsValue({ materials: [undefined] })
+            }
+        }
+    }, [value, visible])
+
+    // 处理判断有哪些素材 展示按钮
+    useEffect(() => {
+        console.log('material----->', material);
+        if (material?.length > 0) {
+            setVideoUploads(material?.find((item: { name: string }) => item.name === 'short_video1' || item.name === 'video'))
+            setImgUploads(material?.find((item: { name: string }) => item.name === 'image' || item.name === 'image_list'))
+        } else {
+            setVideoUploads({})
+            setImgUploads({})
+        }
+    }, [material])
+
+    // 确定
+    const handleOk = () => {
+        form.validateFields().then(values => {
+            let data = values?.materials?.map((item: any) => {
+                let data: any = {}
+                for (const key in item) {
+                    switch (key) {
+                        case 'image'://图素材
+                            data.imageUrl = item[key]
+                            break;
+                        case 'video'://视频素材
+                            data.videoUrl = item[key]
+                            break;
+                        case 'image_list'://图素材
+                            data.imageUrlList = item[key]
+                            break;
+                        case 'short_video1'://视频素材
+                            data.shortVideo1Url = item[key]
+                            break;
+                    }
+                }
+                return data
+            })
+            onChange && onChange(data)
+            setVisible(false)
+        })
+    }
+
+    return <>
+        <span onClick={() => { setVisible(true) }}>{value && value?.length > 0 ? '编辑' : '添加'}</span>
+        {visible && <Modal
+            visible={visible}
+            onCancel={() => setVisible(false)}
+            title={<>
+                <span>创意素材</span>
+                {videoUploads && Object.keys(videoUploads)?.length > 0 && <Button type="link" onClick={() => {
+                    console.log('videoUploads---->', videoUploads);
+                    init({ mediaType: 'VIDEO', num: 100, cloudSize: adcreativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: videoUploads.restriction.videoRestriction.minWidth, height: videoUploads.restriction.videoRestriction.minHeight }]], maxSize: videoUploads.restriction.videoRestriction.fileSize * 1024 })
+                    setMaterialConfig({
+                        ...materialConfig,
+                        type: videoUploads.name,
+                        max: 1,
+                        index: 99999,
+                        adcreativeTemplateId
+                    })
+                    setTimeout(() => {
+                        set_selectVideoVisible(true)
+                    }, 100)
+                }}>批量添加视频素材</Button>}
+                {imgUploads && Object.keys(imgUploads)?.length > 0 && <Button type="link" onClick={() => {
+                    init({ mediaType: 'IMG', num: 100, cloudSize: [[{ relation: '=', width: imgUploads.restriction.imageRestriction.width, height: imgUploads.restriction.imageRestriction.height }]], maxSize: imgUploads.restriction.imageRestriction.fileSize * 1024 })
+                    setMaterialConfig({
+                        ...materialConfig,
+                        type: imgUploads.name,
+                        max: imgUploads.name === 'image_list' ? imgUploads.arrayProperty.maxNumber : 1,
+                        index: 99999,
+                        adcreativeTemplateId
+                    })
+                    setTimeout(() => {
+                        set_selectVideoVisible(true)
+                    }, 100)
+                }}>批量添加图片素材</Button>}
+            </>}
+            width={930}
+            onOk={handleOk}
+        >
+            <Form name="dynamic_form_item" form={form} labelAlign='left' >
+                <Form.List name="materials">
+                    {(fields, { add, remove }) => (<>
+                        {fields.map((field, num) => (<div key={field.key}>
+                            <Space size={30} style={{ width: '100%' }} className={styles.space}>
+                                {material?.map((item, index) => {
+                                    if (item.name === 'short_video1' || item.name === 'video') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${styles.video}`} style={{ width: 300, height: 160 }} onClick={() => {
+                                                init({ mediaType: 'VIDEO', cloudSize: adcreativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: item.restriction.videoRestriction.minWidth, height: item.restriction.videoRestriction.minHeight }]], maxSize: item.restriction.videoRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: 1,
+                                                    index: num,
+                                                    adcreativeTemplateId
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+                                                }, 100)
+                                            }}>
+                                                <p>
+                                                    {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <video src={materials[num][item.name]} controls /> : <>
+                                                        <span>{`推荐尺寸(${adcreativeTemplateId === 1708 ? 1280 : item.restriction.videoRestriction.minWidth} x ${adcreativeTemplateId === 1708 ? 720 : item.restriction.videoRestriction.minHeight})`}</span>
+                                                        <span>{`${item.restriction.videoRestriction.fileFormat?.map((str: any) => str?.replace('MEDIA_TYPE_', ''))};< ${item.restriction.videoRestriction.fileSize / 1024}M;时长 ≥ ${item.restriction.videoRestriction.minDuration}s,≤ ${item.restriction.videoRestriction.maxDuration}s,必须带有声音`}</span>
+                                                    </>}
+                                                </p>
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    if (item.name === 'image') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
+                                                init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: 1,
+                                                    index: num,
+                                                    adcreativeTemplateId
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+                                                }, 100)
+                                            }}>
+                                                <p>
+                                                    {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) ? <img src={materials[num][item.name]} /> : <>
+                                                        <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
+                                                        <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
+                                                    </>}
+                                                </p>
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    if (item.name === 'image_list') {
+                                        return <Form.Item
+                                            {...field}
+                                            label={<strong>{item.label}</strong>}
+                                            rules={[{ required: true, message: '请选择素材!' }]}
+                                            name={[field.name, item.name]}
+                                            key={index}
+                                        >
+                                            <div className={`${styles.box} ${item.arrayProperty.maxNumber >= 3 ? styles.image_list : styles.imageMater}`} style={item.arrayProperty.maxNumber >= 3 ? { flexFlow: 'row', width: '100%' } : {}} onClick={() => {
+                                                init({ mediaType: 'IMG', num: item.arrayProperty.maxNumber, cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
+                                                setMaterialConfig({
+                                                    ...materialConfig,
+                                                    type: item.name,
+                                                    max: item.arrayProperty.maxNumber,
+                                                    index: num,
+                                                    adcreativeTemplateId
+                                                })
+                                                setTimeout(() => {
+                                                    set_selectVideoVisible(true)
+                                                }, 100)
+                                            }}>
+                                                {Array(item.arrayProperty.maxNumber).fill('').map((arr, index1) => {
+                                                    return <p key={index1} style={item.arrayProperty.maxNumber >= 3 ? { width: 130, height: 130 } : { width: 130, height: 130, justifyContent: 'center' }}>
+                                                        {materials?.length > 0 && materials[num] && Object.keys(materials[num])?.includes(item.name) && materials[num][item.name][index1] ? <img src={materials[num][item.name][index1]} /> : <>
+                                                            <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
+                                                            <span>{`${item.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
+                                                        </>}
+                                                    </p>
+                                                })}
+                                            </div>
+                                        </Form.Item>
+                                    }
+                                    return null
+
+                                })}
+                                {fields?.length > 1 && <MinusCircleOutlined className={styles.clear} onClick={() => remove(field.name)} style={{ marginBottom: 24, color: 'red' }} />}
+                            </Space>
+                        </div>))}
+                        <Form.Item>
+                            <Button type="link" onClick={() => add()} icon={<PlusOutlined />} style={{ padding: 0 }}>
+                                新增素材组
+                            </Button>
+                        </Form.Item>
+                    </>)}
+                </Form.List>
+            </Form>
+        </Modal>}
+
+        {/* 选择视频素材 */}
+        {selectVideoVisible && <SelectCloud
+            visible={selectVideoVisible}
+            onClose={() => set_selectVideoVisible(false)}
+            sliderImgContent={materialConfig.index === 99999 ? undefined : materialConfig.type === 'image_list'
+                ? (materials[materialConfig.index] && Object.keys(materials[materialConfig.index])?.includes('image_list')) ? materials[materialConfig.index]['image_list']?.map((item: string) => ({ url: item })) : undefined
+                : (materials[materialConfig.index] && Object.keys(materials[materialConfig.index])?.includes(materialConfig.type)) ? [{ url: materials[materialConfig.index][materialConfig.type] }] : undefined
+            }
+            onChange={(content: any) => {
+                if (content.length > 0) {
+                    if (materialConfig.index === 99999) {
+                        if (materialConfig.type === 'image_list') {
+                            let urls = content?.map((item: any) => item?.url)
+                            let max = materialConfig.max
+                            let materialsNew = materials.map((item: any) => {
+                                let newItem = item || {}
+                                // 判断是否有字段,是否设置了值
+                                if (Object.keys(newItem).includes(materialConfig.type) && newItem[materialConfig.type]) {
+                                    if (max > newItem[materialConfig.type].length && urls.length > 0) {
+                                        let difference = max - newItem[materialConfig.type].length
+                                        let material: any[] = []
+                                        if (urls.length >= difference) {
+                                            material = urls.splice(0, difference)
+                                        } else {
+                                            material = urls.splice(0, urls.length)
+                                        }
+                                        newItem[materialConfig.type] = [...newItem[materialConfig.type], ...material]
+                                        return newItem
+                                    } else {
+                                        return newItem
+                                    }
+                                } else {
+                                    if (urls.length >= max) {
+                                        let material = urls.splice(0, max)
+                                        return { ...newItem, [materialConfig.type]: material }
+                                    } else if (urls.length > 0) {
+                                        let material = urls.splice(0, urls.length)
+                                        return { ...newItem, [materialConfig.type]: material }
+                                    } else {
+                                        return newItem
+                                    }
+                                }
+                            })
+                            if (urls.length > 0) {
+                                let data = Array(Math.ceil(urls.length / max)).fill(undefined).map(item => {
+                                    if (urls.length >= max) {
+                                        let material = urls.splice(0, max)
+                                        return { [materialConfig.type]: material }
+                                    } else {
+                                        let material = urls.splice(0, urls.length)
+                                        return { [materialConfig.type]: material }
+                                    }
+                                })
+                                materialsNew = [...materialsNew, ...data]
+                            }
+                            form.setFieldsValue({ materials: materialsNew })
+                        } else {
+                            let newMaterials = content?.map((item: any) => ({ [materialConfig.type]: item?.url }))
+                            if (newMaterials.length > 0) {
+                                if (materials?.every((item: any) => !item)) { // 没设置过
+                                    form.setFieldsValue({ materials: newMaterials })
+                                } else { // 设置过
+                                    let materialsNew = materials.map((item: any) => {
+                                        let newItem = item || {}
+                                        if (Object.keys(newItem).includes(materialConfig.type) && newItem[materialConfig.type]) {
+                                            return item
+                                        } else {
+                                            if (newMaterials.length > 0) {
+                                                let material = newMaterials.splice(0, 1)
+                                                return { ...newItem, ...material[0] }
+                                            } else {
+                                                return item
+                                            }
+                                        }
+                                    })
+                                    if (newMaterials.length > 0) {
+                                        materialsNew = [...materialsNew, ...newMaterials]
+                                    }
+                                    form.setFieldsValue({ materials: materialsNew })
+                                }
+                            }
+                        }
+                    } else {
+                        materials = materials?.map((item: any, index: number) => {
+                            if (materialConfig.index === index) {
+                                if (materialConfig.type === 'image_list') {
+                                    if (item) {
+                                        item[materialConfig.type] = content?.map((item: any) => item?.url)
+                                        return { ...item }
+                                    } else {
+                                        return { [materialConfig.type]: content?.map((item: any) => item?.url) }
+                                    }
+                                } else {
+                                    if (item) {
+                                        item[materialConfig.type] = content[0]?.url
+                                        return { ...item }
+                                    } else {
+                                        return { [materialConfig.type]: content[0]?.url }
+                                    }
+                                }
+                            }
+                            return item
+                        })
+                        form.setFieldsValue({ materials })
+                    }
+                }
+                set_selectVideoVisible(false)
+            }}
+        />}
+        {/* 视频封面图弹窗 */}
+        {videoImgsVisbile && <Modal
+            visible={videoImgsVisbile}
+            title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
+            onOk={() => {
+                if (videoImgs.activeUrl) {
+                    // setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
+                    set_videoImgsVisbile(false)
+                } else {
+                    message.error('请选择图片,获取使用取消按钮关闭弹窗!')
+                }
+            }}
+            onCancel={() => { set_videoImgsVisbile(false) }}
+            confirmLoading={getVideoCapture.loading}
+            width={600}
+        >
+
+            <Radio.Group className={styles.videoImgs} onChange={(e) => {
+                let url = e.target.value
+                set_videoImgs({ ...videoImgs, activeUrl: url })
+            }}>
+                {
+                    videoImgs?.urlList?.map((item: any, index: number) => {
+                        return <Radio.Button value={item} key={index}>
+                            <Image src={item} preview={videoImgs.preview} />
+                        </Radio.Button>
+                    })
+                }
+            </Radio.Group>
+        </Modal>}
+    </>
+}
+
+export default React.memo(Material)

+ 152 - 0
src/pages/launchSystemNew/launchManage/createAd/creativeCL/modal/tableConfig.tsx

@@ -0,0 +1,152 @@
+import React from "react"
+import { Image, Popconfirm, Space } from 'antd'
+
+let brandColumns = (del: (id: number) => void, edit: (data: any) => void) => {
+
+
+    let data: any[] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a onClick={() => edit(b)}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red' }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 60,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '头像预览图',
+            dataIndex: 'brandImgUrl',
+            key: 'brandImgUrl',
+            width: 120,
+            ellipsis: true,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <Image width={40} style={{ borderRadius: 4 }} src={a} />
+            }
+        },
+        {
+            title: '头像名称',
+            dataIndex: 'name',
+            key: 'name',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+let profileColumns = (del: (id: number) => void, edit: (data: any) => void) => {
+
+
+    let data: any[] = [
+        {
+            title: '操作',
+            dataIndex: 'cz',
+            key: 'cz',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                return <Space>
+                    <a onClick={() => edit(b)}>修改</a>
+                    <Popconfirm
+                        title="确定删除?"
+                        onConfirm={() => del(b.id)}
+                        okText="是"
+                        cancelText="否"
+                    >
+                        <a style={{ color: 'red' }}>删除</a>
+                    </Popconfirm>
+                </Space>
+            }
+        },
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            width: 60,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '头像预览图',
+            dataIndex: 'headImageUrl',
+            key: 'headImageUrl',
+            width: 120,
+            ellipsis: true,
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <Image width={40} style={{ borderRadius: 4 }} src={a} />
+            }
+        },
+        {
+            title: '头像名称',
+            dataIndex: 'profileName',
+            key: 'profileName',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '详细描述',
+            dataIndex: 'description',
+            key: 'description',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a}</span>
+            }
+        }
+    ]
+
+    return data
+}
+
+export {
+    brandColumns,
+    profileColumns
+}

+ 67 - 3
src/pages/launchSystemNew/launchManage/createAd/index.less

@@ -5,6 +5,7 @@
     font-weight: 600;
   }
 }
+
 // #page_tabs,#ad_tabs{
 //   >div>div>div>div{
 //     padding: 5px;
@@ -69,6 +70,7 @@
         box-sizing: border-box;
         display: flex;
         flex-direction: column;
+        position: relative;
 
         .top {
           height: 24px;
@@ -84,7 +86,8 @@
             font-size: 12px;
             color: rgb(90, 90, 90);
           }
-          >a{
+
+          >a {
             font-size: 12px;
             font-weight: 400;
           }
@@ -133,12 +136,14 @@
 
             .close {
               cursor: pointer;
-              margin-right: 10px;
+              margin-right: 4px;
               display: none;
               position: absolute;
               right: 4px;
               top: 50%;
               transform: translateY(-50%);
+              color: #0e95f6;
+              box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
             }
 
             &:hover {
@@ -157,6 +162,8 @@
           line-height: 40px;
           display: flex;
           justify-content: center;
+          
+
           span {
             cursor: pointer;
             color: #108ee9;
@@ -166,6 +173,22 @@
             box-shadow: 0 0 4px 1px rgba(0, 0, 0, 0.08);
           }
         }
+
+        .noMaterial {
+          position: absolute;
+          background-color: rgba(0, 0, 0, .1);
+          top: 0;
+          left: 0;
+          right: 0;
+          bottom: 0;
+          width: 100%;
+          height: 100%;
+          display: flex;
+          justify-content: center;
+          align-items: center;
+          cursor: no-drop;
+          color: red;
+        }
       }
     }
   }
@@ -177,6 +200,7 @@
     justify-content: flex-end;
   }
 }
+
 .popoverContent {
   border: 1px solid #dcdee2;
   padding: 5px;
@@ -184,11 +208,13 @@
   transition: all 0.5s;
   margin-top: 1px;
   margin-bottom: 5px;
+
   &:hover {
     border-color: rgb(24, 144, 255);
     box-shadow: 0 0 4px 1px rgba(24, 144, 255, 0.4);
   }
 }
+
 .popover {
   max-width: 450px;
   max-height: 150px;
@@ -204,4 +230,42 @@
   -webkit-line-clamp: 2;
   line-clamp: 2;
   -webkit-box-orient: vertical;
-}
+}
+
+
+.group {
+  display: flex;
+  justify-content: space-between;
+  gap: 5px;
+  margin-bottom: 10px;
+
+  >div {
+    flex: 1;
+
+    >img {
+      width: 100%;
+    }
+
+    >video {
+      width: 100%;
+    }
+  }
+}
+
+.otherGroup {
+  display: flex;
+  justify-content: space-between;
+  flex-wrap: wrap;
+
+  .group {
+    width: 35%;
+
+    >img {
+      width: 100%;
+    }
+
+    >video {
+      width: 100%;
+    }
+  }
+}

+ 382 - 103
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -6,8 +6,8 @@ import { PromotedObjectType } from "@/services/launchAdq/enum"
 import { getTagsList } from "@/services/launchAdq/global"
 import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
 import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
-import { CloseOutlined, SearchOutlined } from "@ant-design/icons"
-import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs } from "antd"
+import { CheckOutlined, CloseOutlined, SearchOutlined } from "@ant-design/icons"
+import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs, Popconfirm, notification } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import { useModel } from "umi"
 import Ad from "./ad"
@@ -17,7 +17,6 @@ import IdModal from "../../components/idModal"
 import LookLanding from "../../components/lookLanding"
 import PageModal from "../../components/pageModal"
 import SelectCloud from "../../components/selectCloud"
-import { WxAutoButton } from "../../req"
 import style from './index.less'
 import Selector from "./selector"
 import SubmitModal from "./submitModal"
@@ -26,6 +25,9 @@ import TargetIng from './targeting'
 import Creative from './creative'
 import AddGroup from '../../components/addGroup'
 import CustomerServiceModal from "../../components/customerServiceModal"
+import { getTaskDetailsApi } from "@/services/launchAdq/taskList"
+import CreativeCL from "./creativeCL"
+import { groupBy } from "@/utils/utils"
 
 const CreateAd: React.FC = () => {
 
@@ -49,7 +51,8 @@ const CreateAd: React.FC = () => {
         expandEnabled: false,
         expandTargeting: []
     })
-    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: number, productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([])  // 账户
+    const [launchMode, setLaunchMode] = useState<number>(Number(localStorage.getItem('LAUNCHMODE')) || 1) // 投放模式 1 现在投放模式  2 创量模式
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: any[], productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([])  // 账户
     const { currentUser: { userId } }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
 
     const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
@@ -76,15 +79,111 @@ const CreateAd: React.FC = () => {
     const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
     const getSysAdcreative = useAjax((params) => getSysAdcreativeInfo(params))
     const createAdBatch = useAjax((params) => createAdBatchApi(params))
+    const getTaskDetails = useAjax((params) => getTaskDetailsApi(params))
     /*************************/
 
     /**数据回填 */
     useEffect(() => {
-        let adqAdData = localStorage.getItem('ADQAD')
-        if (adqAdData) {
-            const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
-            setQueryForm({ ...queryForm })
-            setAccountCreateLogs(accountCreateLogs)
+        let taskId = sessionStorage.getItem('TASKID')
+        if (taskId) {
+            getTaskDetails.run(taskId).then(res => {
+                console.log('res----->', res)
+                setLaunchMode(1)
+                const { adCreateLogs, campaignType, promotedObjectType, speedMode, sysAdgroup, sysAdgroupId, sysTargeting, sysTargetingId } = res
+                let adCreateLogsData = adCreateLogs?.map((item: any) => {
+                    return {
+                        adAccountId: item?.accountId,
+                        id: item?.adAccountId,
+                        userActionSetsList: item?.userActionSetList?.map((item: any) => ({ ...item, id: item?.userActionSetId })),
+                        productList: item?.product ? [{ ...item?.product, productCatalog: item?.productCatalog, id: Number(item?.product?.productOuterId?.replace(/\D/ig, '')) }] : undefined,
+                        coldStartAudienceList: item?.coldStartAudienceList?.map((item: any) => ({ ...item, id: item.audienceId }))
+                        // pageList: [item.page]
+                    }
+                }).filter((item: any, index: number, self: any) => self.findIndex((i: any) => i.id == item.id) === index)
+
+                setAccountCreateLogs(adCreateLogsData)
+                const sorted = groupBy(adCreateLogs, (item) => [item.sysAdcreativeId])
+                let taskMediaMaps = sorted[0]?.map((item: any) => {
+                    let pageElementsSpecList = item?.sysPage?.pageSpecsList[0]?.pageElementsSpecList // 内容区
+                    let globalSpec = item?.sysPage?.globalSpec  // 悬浮组件
+                    /** 处理客服 */
+                    let cropUserGroupMap: any[] = []
+                    if ((pageElementsSpecList as any[])?.some((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX') || (globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList?.some((item: { floatButtonSpec: { elementType: string } }) => item?.floatButtonSpec?.elementType === 'ENTERPRISE_WX'))) {
+                        let groupList: { type: number, name: string, cropList: any[], cropId?: number, groupId?: number }[] = [];
+                        (pageElementsSpecList as any[])?.forEach((item: { elementType: string, enterpriseWxSpec: { btnTitle: string } }) => {
+                            if (item?.elementType === 'ENTERPRISE_WX') {
+                                groupList.push({ type: 1, name: '联系商家', cropList: [] }) // item.enterpriseWxSpec.btnTitle
+                            }
+                        })
+                        if ((globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList)) {
+                            groupList.push({ type: 2, name: '悬浮组件', cropList: [] })
+                        }
+                        cropUserGroupMap = sorted[0]?.map((item: any) => {
+                            let corpUserGroup1s = item?.corpUserGroup1s
+                            let corpUserGroup2s = item?.corpUserGroup2s
+                            return {
+                                adAccountId: item.accountId, id: item.adAccountId, data: groupList.map((crop: any, index: number) => {
+                                    return { ...crop, cropList: crop.type === 1 ? corpUserGroup1s[index] ? [{ ...corpUserGroup1s[index], id: corpUserGroup1s[index].groupId }] : [] : corpUserGroup2s[0] ? [{ ...corpUserGroup2s[0], id: corpUserGroup2s[0].groupId }] : []}
+                                })
+                            }
+
+                        })
+                    }
+                    let accountPageIdMap: any = {}
+                    adCreateLogs?.forEach((item: any) => {
+                        if (item?.pageId) {
+                            accountPageIdMap[item.accountId] = item?.pageId
+                        }
+                    })
+                    return { sysAdcreative: item?.sysAdcreative, sysPageId: item?.sysPageId, cropUserGroupMap, accountPageIdMap }
+                })
+
+                /** 云端落地页处理 */
+                let pageList = sorted?.map((item: any[]) => {
+                    if (item.some((item1: { sysPageId: number }) => item1.sysPageId)) {
+                        return item[0].sysPage
+                    } else {
+                        return null
+                    }
+                })
+                let adqPageList = sorted?.map((item: any[]) => {
+                    if (item.some((item1: { pageId: number }) => item1.pageId)) {
+                        return item.map((item1: any) => ({
+                            pageList: [{ ...item1.page, id: item1.page.pageId }],
+                            adAccountId: item1?.accountId,
+                            id: item1?.adAccountId,
+                        }))
+                    } else {
+                        return null
+                    }
+                })
+
+                setQueryForm({
+                    ...queryForm,
+                    campaignType,
+                    promotedObjectType,
+                    speedMode,
+                    sysAdgroup,
+                    sysAdgroupId,
+                    sysTargeting,
+                    sysTargetingId,
+                    adgroupName: sysAdgroup?.adgroupName,
+                    configuredStatus: sysAdgroup?.configuredStatus,
+                    expandEnabled: sysAdgroup?.expandEnabled || false,
+                    expandTargeting: sysAdgroup?.expandTargeting || [],
+                    taskMediaMaps: taskMediaMaps || [],
+                    pageList,
+                    adqPageList
+                })
+            })
+            sessionStorage.removeItem('TASKID')
+        } else {
+            let adqAdData = localStorage.getItem('ADQAD')
+            if (adqAdData) {
+                const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
+                setQueryForm({ ...queryForm })
+                setAccountCreateLogs(accountCreateLogs)
+            }
         }
     }, [])
     // 设置地域
@@ -112,6 +211,31 @@ const CreateAd: React.FC = () => {
         getAdAccount.run()
     }, [])
 
+    // 账号对比
+    useEffect(() => {
+        if (getAdAccount?.data?.data && accountCreateLogs) {
+            if (accountCreateLogs.some(item => !getAdAccount?.data?.data?.find((item1: { id: number, accountId: number }) => item.id === item1.id && item.adAccountId == item1.accountId))) {
+                let errorData: any[] = []
+                let newAccountCreateLogs = accountCreateLogs.filter(item => {
+                    let data = getAdAccount?.data?.data?.find((item1: { id: number, accountId: number }) => item.id === item1.id && item.adAccountId == item1.accountId)
+                    if (data) {
+                        return true
+                    } else {
+                        errorData.push(item.adAccountId)
+                        return false
+                    }
+                })
+                notification.error({
+                    duration: 60 * 5,
+                    message: '重要提示',
+                    description: `本地媒体账户与你所拥有账户对不上,当前创建账号不符合账号及部分相关配置已清空。请把保存在本地的媒体账户或者媒体账户组清空,重新选择保存。问题账户:(${errorData.toString()})`
+                })
+                setAccountCreateLogs(newAccountCreateLogs)
+                setQueryForm({ ...queryForm, adqPageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map(item => ({ ...item, accountPageIdMap: {} })) })
+            }
+        }
+    }, [getAdAccount?.data, accountCreateLogs, queryForm])
+
     /** 获取广告详情 */
     useEffect(() => {
         if (getSysAdgroups?.data?.bidMode !== 'BID_MODE_CPM' && accountCreateLogs?.length > 0) {
@@ -126,8 +250,6 @@ const CreateAd: React.FC = () => {
     }, [getSysAdgroups?.data?.bidMode])
 
 
-
-
     /** 删除商品内容 */
     const goodsDel = (index: number) => {
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
@@ -135,13 +257,6 @@ const CreateAd: React.FC = () => {
         setAccountCreateLogs(newArr)
     }
 
-    /** 删除云端内容 */
-    const pageDel = (index: number) => {
-        let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
-        delete newArr[index].pageList
-        setAccountCreateLogs(newArr)
-    }
-
     /** 删除数据源 */
     const sourceDel = (index: number, num: number) => {
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
@@ -156,36 +271,60 @@ const CreateAd: React.FC = () => {
         setAccountCreateLogs(newArr)
     }
 
+    // 创意素材与文案叉乘处理
+    const whatever = (...arrs: any[]) => {
+        console.log('arrs---->', arrs);
+        if (arrs[0]?.length && arrs[1]?.length) {
+            return arrs.reduce((total, curr) => total.flatMap((e: any) => curr.map((e2: any) => ({ ...e2, ...e }))))
+        } else if (arrs[0]?.length) {
+            return arrs[0]
+        } else if (arrs[1]?.length) {
+            return arrs[1]
+        } else {
+            return ['']
+        }
+    }
 
 
     /** 预览 */
     const preview = () => {
+        let newQueryForm: Partial<CreateAdProps> = JSON.parse(JSON.stringify(queryForm))
         if (accountCreateLogs?.length === 0) {
             message.error('请选择媒体账户')
             return
         }
-        if (!queryForm.promotedObjectType) {
+        if (!newQueryForm.promotedObjectType) {
             message.error('请选择推广目标')
             return
         }
-        if (!queryForm.sysAdgroup) {
+        if (!newQueryForm.sysAdgroup) {
             message.error('请先设置广告基本信息')
             return
         }
-        if (!queryForm.sysTargeting) {
+        if (!newQueryForm.sysTargeting) {
             message.error('请选择定向')
             return
         }
-        if (!queryForm.taskMediaMaps?.every(item => item.sysAdcreative)) {
+        if (!newQueryForm.taskMediaMaps?.every(item => item.sysAdcreative)) {
             message.error('请设置创意的基本信息')
             return
         }
-        if (!queryForm.taskMediaMaps?.every(item => item.sysPageId || item.accountPageIdMap)) {
+        if (!newQueryForm.taskMediaMaps?.every(item => item.sysPageId || item.accountPageIdMap)) {
             message.error('请选择落地页')
             return
         }
-        if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps?.some((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)) {
-            let cropData = queryForm?.taskMediaMaps?.filter((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)
+        if (launchMode === 2) {
+            if ((queryForm?.materialData && queryForm?.materialData?.length > 0) && !(newQueryForm?.materials && newQueryForm?.materials?.length > 0)) {
+                message.error('请选择创意素材')
+                return
+            }
+            if ((queryForm?.textData && queryForm.textData?.length > 0) && !(newQueryForm?.texts && newQueryForm?.texts?.length > 0)) {
+                message.error('请选择创意文案')
+                return
+            }
+        }
+        if (newQueryForm?.taskMediaMaps && newQueryForm?.taskMediaMaps?.some((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)) {
+            let cropData = newQueryForm?.taskMediaMaps?.filter((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)
             if (cropData?.some((item: { cropUserGroupMap: { data: { cropList: any[] }[] }[] }) => {
                 return item?.cropUserGroupMap?.some((item1: { data: { cropList: any[] }[] }) => item1?.data?.some((item2: { cropList: any[] }) => item2?.cropList?.length === 0))
             })) {
@@ -194,27 +333,51 @@ const CreateAd: React.FC = () => {
             }
         }
         let data: any[] = []
+        if (launchMode === 2) {
+            if (Array.isArray(newQueryForm.materials) && Array.isArray(newQueryForm?.texts)) {
+                let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
+                let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
+                let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
+                    taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
+                    return JSON.parse(JSON.stringify(taskMediaMap))
+                })
+                newQueryForm.taskMediaMaps = newTaskMediaMaps
+            }
+        }
         accountCreateLogs.forEach((item: any) => {
-            queryForm.taskMediaMaps?.forEach((task, index) => {
+            newQueryForm.taskMediaMaps?.forEach((task, index) => {
                 let obj = {
                     ...item,
-                    ...queryForm,
-                    sysAdGroupData: queryForm.sysAdgroup,
-                    targetingData: queryForm.sysTargeting,
+                    ...newQueryForm,
+                    sysAdGroupData: newQueryForm.sysAdgroup,
+                    targetingData: newQueryForm.sysTargeting,
                     sysAdcreativeData: task.sysAdcreative,
-                    pageData: (queryForm.pageList as any)[index] || (queryForm.adqPageList as any)[index]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0],
+                    pageData: launchMode === 2 ? (newQueryForm.pageList as any)[0] || (newQueryForm.adqPageList as any)[0]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0] : (newQueryForm.pageList as any)[index] || (newQueryForm.adqPageList as any)[index]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0],
                     myId: Number(item.id + '' + index)
                 }
                 data.push(obj)
             })
         })
-        console.log('tableData--->', data)
         setTableData(data)
     }
 
     const submit = (props: { campaignName: string, count?: number }) => {
-        console.log(111111, tableSelect);
         let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+        if (launchMode === 2) {
+            if (Array.isArray(newQueryForm.materials) && Array.isArray(newQueryForm?.texts)) {
+                let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
+                let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
+                let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
+                    if (item) {
+                        taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
+                    } else {
+                        taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements }
+                    }
+                    return JSON.parse(JSON.stringify(taskMediaMap))
+                })
+                newQueryForm.taskMediaMaps = newTaskMediaMaps
+            }
+        }
         let newtaskMediaMaps = newQueryForm.taskMediaMaps.map((item1: { cropUserGroupMap?: any[] }) => {
             let { cropUserGroupMap, ...data } = item1
             if (cropUserGroupMap && cropUserGroupMap?.length > 0) {
@@ -271,6 +434,7 @@ const CreateAd: React.FC = () => {
         })
         if (params?.expandEnabled) {
             params.sysAdgroup.expandEnabled = params?.expandEnabled
+            params.sysAdgroup.expandTargeting = []
         }
         if (params?.expandTargeting?.length > 0) {
             params.sysAdgroup.expandTargeting = params?.expandTargeting
@@ -284,8 +448,11 @@ const CreateAd: React.FC = () => {
         delete params.count
         delete params?.expandEnabled
         delete params?.expandTargeting
+        delete params?.texts
+        delete params?.textData
+        delete params?.materialData
+        delete params?.materials
         console.log('paramsSubmit====>', params)
-
         createAdBatch.run(params).then(res => {
             if (res) {
                 sessionStorage.setItem('CAMP', props?.campaignName)
@@ -444,11 +611,34 @@ const CreateAd: React.FC = () => {
         setUsersArr(data)
     }, [])
 
-    console.log('queryForm111111', queryForm);
+    // 切换投放模式
+    const switchLaunchMode = () => {
+        if (launchMode === 1) {
+            setLaunchMode(2)
+            localStorage.setItem('LAUNCHMODE', '2')
+        } else {
+            setLaunchMode(1)
+            localStorage.setItem('LAUNCHMODE', '1')
+        }
+        delBdPlan()
+        set_targetKey('0')
+    }
+
+    console.log('queryForm111111---->', queryForm, accountCreateLogs);
 
     return <Space direction="vertical" style={{ width: '100%' }}>
         <Card
-            title={<div className={style.cardTitle}>配置区</div>}
+            title={<Space>
+                <div className={style.cardTitle}>配置区</div>
+                <Popconfirm
+                    title="数据部分不会保存,是否切换?"
+                    onConfirm={switchLaunchMode}
+                    okText="是"
+                    cancelText="否"
+                >
+                    <Button type="link" style={{ padding: 0 }}>切换投放模式</Button>
+                </Popconfirm>
+            </Space>}
             className={style.createAd}
             hoverable
             extra={<AddGroup onChange={usersChange} pitcherData={getAdAccount?.data?.data} />}
@@ -496,7 +686,7 @@ const CreateAd: React.FC = () => {
                         value={accountCreateLogs?.map((item: { id: number }) => item?.id)}
                         onChange={(e, option) => {
                             console.log(option)
-                            setQueryForm({ ...queryForm, taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '' })) })
+                            setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
                             setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children?.toString()?.split('——')[0], id: item?.value })))
                             clearData()
                         }}
@@ -507,17 +697,21 @@ const CreateAd: React.FC = () => {
                 <Selector label="推广目标">
                     <Select style={{ width: 200 }} value={queryForm?.promotedObjectType} placeholder="请选择推广目标" bordered={false} showSearch filterOption={(input: any, option: any) =>
                         (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
-                    } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e, sysAdgroup: null, sysAdgroupId: undefined, taskMediaMaps: [], sysAdcreativeId: undefined }); clearData() }}>
+                    } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e, sysAdgroup: null, sysAdgroupId: undefined, taskMediaMaps: [], sysAdcreativeId: undefined, materials: [], textData: [], texts: [] }); clearData() }}>
                         {Object.keys(PromotedObjectType).map(key => {
                             return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
                         })}
                     </Select>
                 </Selector>
+                {launchMode === 2 && accountCreateLogs?.length > 0 && <>
+                    <Button onClick={() => { setGoodsVisible(true) }}>商品广告(选填){accountCreateLogs?.some(item => item?.productList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
+                    <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
+                </>}
             </Space>
 
             <div className={style.cardBody}>
                 <Row className={style.content}>
-                    <Col span={12} className={style.conLeft}>
+                    <Col span={launchMode === 1 ? 12 : 8} className={style.conLeft}>
                         <Row className={`${style.conTitle} ${style.conRightBorder}`}><Col span={24}>广告</Col></Row>
                         <Row className={style.items}>
                             {/* =============广告基本信息=========== */}
@@ -535,73 +729,76 @@ const CreateAd: React.FC = () => {
                                 cpDel={cpDel}
                                 accountCreateLogs={accountCreateLogs}
                             />
-                            {/* =============商品=========== */}
-                            <Col className={style.conRightBorder} span={5}>
-                                <div className={style.top}>
-                                    商品{/* <span>已选:{1}</span> */}
-                                </div>
-                                <div className={style.center}>
-                                    <div className={style.centerContent}>
-                                        {accountCreateLogs?.map((item: any, index: number) => {
-                                            if (item?.productList) {
-                                                return <div className={style.acc} key={index}>
-                                                    <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
-                                                    {
-                                                        item?.productList?.map((pack: { productName: string, author: string, id: number }, index: number) => {
-                                                            return <div className={style.accCon} key={pack.id}>{pack.productName}<CloseOutlined className={style.close} onClick={() => {
-                                                                goodsDel(index)
-                                                            }} /></div>
-                                                        })
-                                                    }
-                                                </div>
-                                            } else {
-                                                return null
-                                            }
-                                        })}
+
+                            {launchMode === 1 && <>
+                                {/* =============商品=========== */}
+                                <Col className={style.conRightBorder} span={5}>
+                                    <div className={style.top}>
+                                        商品
                                     </div>
-                                </div>
-                                <div className={style.bottom}>
-                                    {accountCreateLogs?.length > 0 ? <span onClick={() => { setGoodsVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
-                                        <span>编辑</span>
-                                    </Tooltip>}
-                                </div>
-                            </Col>
-                            {/* 数据源 */}
-                            <Col className={style.conRightBorder} span={5}>
-                                <div className={style.top}>
-                                    数据源 {/* <span>已选:{1}</span> */}
-                                </div>
-                                <div className={style.center}>
-                                    {/* userActionSetsList */}
-                                    <div className={style.centerContent}>
-                                        {accountCreateLogs?.map((item: any, index: number) => {
-                                            if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
-                                                return <div className={style.acc} key={index}>
-                                                    <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
-                                                    {
-                                                        item?.userActionSetsList?.map((pack: { name: string, type: string, id: number }, index1: number) => {
-                                                            return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.name}{' > '}{pack.type}</span> <CloseOutlined className={style.close} onClick={() => {
-                                                                sourceDel(index, index1)
-                                                            }} /></div>
-                                                        })
-                                                    }
-                                                </div>
-                                            } else {
-                                                return null
-                                            }
-                                        })}
+                                    <div className={style.center}>
+                                        <div className={style.centerContent}>
+                                            {accountCreateLogs?.map((item: any, index: number) => {
+                                                if (item?.productList) {
+                                                    return <div className={style.acc} key={index}>
+                                                        <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                        {
+                                                            item?.productList?.map((pack: { productName: string, author: string, id: number }, index: number) => {
+                                                                return <div className={style.accCon} key={pack.id}>{pack.productName}<CloseOutlined className={style.close} onClick={() => {
+                                                                    goodsDel(index)
+                                                                }} /></div>
+                                                            })
+                                                        }
+                                                    </div>
+                                                } else {
+                                                    return null
+                                                }
+                                            })}
+                                        </div>
                                     </div>
-                                </div>
-                                <div className={style.bottom}>
-                                    {accountCreateLogs?.length > 0 ? <span onClick={() => { setSourceVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
-                                        <span>编辑</span>
-                                    </Tooltip>}
-                                </div>
-                            </Col>
+                                    <div className={style.bottom}>
+                                        {accountCreateLogs?.length > 0 ? <span onClick={() => { setGoodsVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
+                                            <span>编辑</span>
+                                        </Tooltip>}
+                                    </div>
+                                </Col>
+
+                                {/* 数据源 */}
+                                <Col className={style.conRightBorder} span={5}>
+                                    <div className={style.top}>
+                                        数据源
+                                    </div>
+                                    <div className={style.center}>
+                                        <div className={style.centerContent}>
+                                            {accountCreateLogs?.map((item: any, index: number) => {
+                                                if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
+                                                    return <div className={style.acc} key={index}>
+                                                        <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                        {
+                                                            item?.userActionSetsList?.map((pack: { name: string, type: string, id: number }, index1: number) => {
+                                                                return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.name}{' > '}{pack.type?.replace('USER_ACTION_SET_TYPE_', '')}</span> <CloseOutlined className={style.close} onClick={() => {
+                                                                    sourceDel(index, index1)
+                                                                }} /></div>
+                                                            })
+                                                        }
+                                                    </div>
+                                                } else {
+                                                    return null
+                                                }
+                                            })}
+                                        </div>
+                                    </div>
+                                    <div className={style.bottom}>
+                                        {accountCreateLogs?.length > 0 ? <span onClick={() => { setSourceVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
+                                            <span>编辑</span>
+                                        </Tooltip>}
+                                    </div>
+                                </Col>
+                            </>}
                         </Row>
                     </Col>
                     {/* =============广告创意=========== */}
-                    <Col span={12} className={style.conRight}>
+                    {launchMode === 1 ? <Col span={12} className={style.conRight}>
                         <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
                         <Row className={style.items}>
                             {/* 创意 */}
@@ -676,7 +873,7 @@ const CreateAd: React.FC = () => {
                                                 init({ mediaType: 'PAGE', cloudSize: undefined })
                                             }
                                         }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
-                                        
+
                                         {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
                                             setPageVisible(true)
                                             if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
@@ -693,7 +890,89 @@ const CreateAd: React.FC = () => {
                                 </div>
                             </Col>
                         </Row>
-                    </Col>
+                    </Col> : <Col span={16} className={style.conRight}>
+                        <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
+                        <Row className={style.items}>
+                            {/* 创意 */}
+                            <CreativeCL queryForm={queryForm} setQueryForm={setQueryForm} clearData={clearData} getSysAdcreative={getSysAdcreative} targetKey={targetKey} />
+                            {/* 落地页 */}
+                            <Col className={style.conRightBorder} style={{ maxWidth: '25%', border: 'none' }}>
+                                <div className={style.top}>
+                                    落地页
+                                    {(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap?.length > 0) && <CustomerServiceModal data={queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap} onChange={(data) => {
+                                        let newQueryForm = JSON.parse(JSON.stringify(queryForm))
+                                        newQueryForm.taskMediaMaps[targetKey].cropUserGroupMap = data
+                                        setQueryForm(newQueryForm)
+                                    }} />}
+                                </div>
+                                <div className={style.center}>
+                                    {queryForm?.taskMediaMaps?.filter((item, index) => index === 0)?.map((item, index) => {
+                                        return <Spin spinning={get.loading} key={index}>
+                                            <div className={style.centerContent}>
+                                                {item?.sysPageId || item?.accountPageIdMap ? <>
+                                                    {(item?.sysPageId && queryForm?.pageList) && <>
+                                                        <div>落地页名称:{queryForm?.pageList[targetKey]?.pageName || ''}</div>
+                                                        <div>分享名称:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareTitle || ''}</div>
+                                                        <div>分享描述:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareDescription || ''}</div>
+                                                        <div style={{ marginBottom: 10 }}>原生推广页顶部素材预览:
+                                                            <div>{queryForm?.pageList[targetKey]?.pageSpecsList && queryForm?.pageList[targetKey]?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
+                                                                switch (item?.elementType) {
+                                                                    case 'TOP_IMAGE':
+                                                                        return <Image width={80} src={item?.topImageSpec?.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
+                                                                    case 'TOP_SLIDER':
+                                                                        return <Space wrap key={index}>
+                                                                            {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
+                                                                        </Space>
+                                                                    case 'TOP_VIDEO':
+                                                                        return <video src={item?.topVideoSpec?.videoUrl} width={150} controls key={index}></video>
+                                                                }
+                                                            })}</div>
+                                                        </div>
+                                                    </>}
+                                                    {queryForm?.adqPageList && queryForm?.adqPageList[targetKey]?.map((adq: any) => {
+                                                        return <div className={style.acc} key={adq.adAccountId}>
+                                                            <div className={style.accName} style={{ fontWeight: 800 }}>{adq.adAccountId}</div>
+                                                            <div className={style.accCon}>
+                                                                <span className={style.title}>{adq.pageList[0].pageName}</span>
+                                                            </div>
+                                                        </div>
+                                                    })}
+                                                </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
+                                            </div>
+                                        </Spin>
+                                    })}
+                                </div>
+                                <div className={style.bottom}>{
+                                    (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative) ? <>
+                                        {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
+                                        <Button type="link" onClick={() => {
+                                            setSelectImgVisible(true)
+                                            // 判定是否用原生页顶部替换外部素材
+                                            if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                                                init({ mediaType: 'PAGE', cloudSize: undefined, adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
+                                            } else {
+                                                init({ mediaType: 'PAGE', cloudSize: undefined })
+                                            }
+                                        }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
+
+                                        {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
+                                            setPageVisible(true)
+                                            if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
+                                                setCloudParams({ adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
+                                            } else {
+                                                setCloudParams({})
+                                            }
+                                        }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
+                                            <Button type="link">云端落地页</Button>
+                                        </Tooltip>}
+                                    </> : <Tooltip title="请先设置创意">
+                                        <Button type="link"><span>选择落地页</span></Button>
+                                    </Tooltip>}
+                                </div>
+                            </Col>
+                        </Row>
+                    </Col>}
+
                 </Row>
                 {/* =============广告底部按钮=========== */}
                 <Space className={style.bts}>
@@ -750,7 +1029,7 @@ const CreateAd: React.FC = () => {
         {/* 选择商品 */}
         {goodsVisible && <GoodsModal visible={goodsVisible} data={accountCreateLogs} onClose={() => setGoodsVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setGoodsVisible(false); clearData() }} />}
         {/* 选择数据源 */}
-        {sourceVisible && <DataSourceModal visible={sourceVisible} data={accountCreateLogs} onClose={() => setSourceVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
+        {sourceVisible && <DataSourceModal visible={sourceVisible} promotedObjectType={queryForm.promotedObjectType} data={accountCreateLogs} onClose={() => setSourceVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
         {/* 选择转化ID */}
         {idVisible && <IdModal visible={idVisible} data={accountCreateLogs} onClose={() => setIdVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
         {/* 选择ADQ落地页 */}

+ 10 - 2
src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx

@@ -21,7 +21,11 @@ let columns = () => {
             align: 'center',
             width: 120,
             render: (a: any, b: any) => {
-                return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.adgroupName}</span>
+                return <div className={style.twoText}>
+                    <Tooltip title={b?.sysAdGroupData?.adgroupName}>
+                        <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.adgroupName}</span>
+                    </Tooltip>
+                </div>
             }
         },
         {
@@ -41,7 +45,11 @@ let columns = () => {
             align: 'center',
             width: 120,
             render: (a: any, b: any) => {
-                return <span style={{ fontSize: "12px" }}>{a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span>
+                return <div className={style.twoText}>
+                    <Tooltip title={a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}>
+                        <span style={{ fontSize: "12px" }}>{a?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span>
+                    </Tooltip>
+                </div>
             }
         },
         {

+ 10 - 3
src/pages/launchSystemNew/launchManage/createAd/targeting/index.tsx

@@ -1,7 +1,7 @@
 import { CreateAdProps } from "@/services/launchAdq/createAd"
 import { BaseResult } from "@ahooksjs/use-request/lib/types"
 import { CloseOutlined, DownOutlined } from "@ant-design/icons"
-import { Button, Col, Dropdown, Empty, Menu, Popover, Space, Spin, Tooltip } from "antd"
+import { Button, Col, Dropdown, Empty, Menu, message, Popover, Space, Spin, Tooltip } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import TargetingModal from "../../../components/targetingModal"
 import TargetingPup from "./modal"
@@ -17,7 +17,7 @@ type Props = {
     setAccountCreateLogs: React.Dispatch<React.SetStateAction<{
         adAccountId: number;
         id: number;
-        userActionSetsList?: number | undefined;
+        userActionSetsList?: any[];
         productList?: any;
         conversionList?: any;
         customAudienceList?: any;
@@ -202,7 +202,14 @@ function TargetIng(props: Props) {
         />}
         {/* 新建定向包 */}
         {adModalConfig.visible && <TargetingPup visible={adModalConfig.visible} PupFn={handleAdModalConfig} callback={(values: any) => {
-            setQueryForm({ ...queryForm, sysTargeting: values, }); setDxVisible(false); clearData();
+            if (queryForm?.expandEnabled && queryForm?.expandTargeting && queryForm?.expandTargeting?.length > 0) {
+                message.error('不可突破定向已重置,需要请重新设置')
+                setQueryForm({ ...queryForm, sysTargeting: values, expandTargeting: [] }); 
+            } else {
+                setQueryForm({ ...queryForm, sysTargeting: values }); 
+            }
+            setDxVisible(false); 
+            clearData();
             handleAdModalConfig({ visible: false, dataInfo: null, type: 'add' })
         }} type={adModalConfig.type} dataInfo={queryForm.sysTargeting} />}
     </Col>

+ 11 - 7
src/pages/launchSystemNew/launchManage/createAd/targeting/modal/index.tsx

@@ -140,12 +140,16 @@ function TargetingPup(props: Props) {
                         delete newValues.excludedActions
                         break;
                     case 'networkType'://联网方式
-                        newValues[key] === '1' && (newValues.targeting.networkType = newValues.network)
+                        if (newValues.network) {
+                            newValues[key] === '1' && (newValues.targeting.networkType = newValues.network)
+                        }
                         delete newValues[key]
                         delete newValues.network
                         break;
                     case 'devicePriceType'://手机价格
-                        newValues[key] === '1' && (newValues.targeting.devicePrice = newValues.devicePrice)
+                        if (newValues.devicePrice) {
+                            newValues[key] === '1' && (newValues.targeting.devicePrice = newValues.devicePrice)
+                        }
                         delete newValues[key]
                         delete newValues.devicePrice
                         break;
@@ -169,7 +173,7 @@ function TargetingPup(props: Props) {
                         delete newValues.userOsAndroid
                         break;
                     case 'excludedDimension'://排除已转化用户
-                        if (newValues[key] !== '0') {
+                        if (newValues[key] !== '0' && newValues.conversionBehaviorList) {
                             newValues.targeting.excludedConvertedAudience = newValues.conversionBehaviorList ? {
                                 excludedDimension: newValues.excludedDimension,
                                 conversionBehaviorList: [newValues.conversionBehaviorList]
@@ -182,8 +186,8 @@ function TargetingPup(props: Props) {
                         break;
                 }
             })
-            // console.log(JSON.stringify(newValues))
-            newValues['isTemplate']=template_checked
+            console.log(JSON.stringify(newValues))
+            newValues['isTemplate'] = template_checked
             // 开启存为模板开关执行
             // if (template_checked && type === 'add') {
             //     create.run(newValues).then(res => {
@@ -192,7 +196,7 @@ function TargetingPup(props: Props) {
             //         }
             //     })
             // } else {
-                callback(newValues)
+            callback(newValues)
             // }
         })
     }, [form, regionsList, template_checked, type])
@@ -297,7 +301,7 @@ function TargetingPup(props: Props) {
                 maritalStatusType: maritalStatus ? '1' : '0',//婚恋
                 maritalStatus,//婚恋
                 excludedDimension: excludedConvertedAudience ? excludedConvertedAudience?.excludedDimension : '0',//排除已转化
-                conversionBehaviorList: excludedConvertedAudience ? excludedConvertedAudience?.conversionBehaviorList[0] : undefined,//排除已转化
+                conversionBehaviorList: excludedConvertedAudience && excludedConvertedAudience?.conversionBehaviorList ? excludedConvertedAudience?.conversionBehaviorList[0] : undefined,//排除已转化
                 deviceBrandModelType: deviceBrandModel ? '1' : '0',//设备品牌型号
                 deviceBrandModelList: deviceBrandModel?.excludedList || deviceBrandModel?.includedList,//设备品牌型号
                 isexcluded: deviceBrandModel && deviceBrandModel?.excludedList,//设备品牌型号

+ 6 - 1
src/pages/launchSystemNew/launchManage/taskList/index.tsx

@@ -34,13 +34,18 @@ const TaskList: React.FC = () => {
         getTaskList.run(queryForm)
     }
 
-    const callback = (data: any, type: 'log' | 'page', allData?: any) => {
+    const callback = (data: any, type: 'log' | 'page' | 'copy', allData?: any) => {
         switch (type) {
             case 'log':
                 setLogData({ ...data })
                 setLogVisible(true)
                 setAllData(allData)
                 break
+            case 'copy':
+                console.log('111111111111--->', data);
+                sessionStorage.setItem('TASKID', data.taskId)
+                window.location.href = '/#/launchSystemNew/launchManage/createAd'
+                break
         }
     }
 

+ 13 - 12
src/pages/launchSystemNew/launchManage/taskList/logTableConfig.tsx

@@ -63,7 +63,7 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
             dataIndex: 'id',
             key: 'id',
             align: 'center',
-            width: 50,
+            width: 65,
             fixed: 'left'
         },
         {
@@ -170,12 +170,24 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
                 }
             }
         },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 160,
+            ellipsis: true,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
         {
             title: '创建状态',
             dataIndex: 'createStatus',
             key: 'createStatus',
             align: 'center',
             width: 90,
+            fixed: 'right',
             render: (a: any, b: any) => {
                 if (a) {
                     return a === 0 ? <Badge status="warning" text={<span style={{ fontSize: "12px" }}>创建中</span>} /> : a === 100 ? <Badge status="success" text={<span style={{ fontSize: "12px" }}>创建成功</span>} /> : <Badge status="error" text={<span style={{ fontSize: "12px" }}>创建失败</span>} />
@@ -184,17 +196,6 @@ function tableConfig(copyCreative: (data: any) => void, callback: (data: any) =>
                 }
             }
         },
-        {
-            title: '创建时间',
-            dataIndex: 'createTime',
-            key: 'createTime',
-            align: 'center',
-            width: 160,
-            ellipsis: true,
-            render: (a: any, b: any) => {
-                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
-            }
-        },
         {
             title: <span style={{ padding: '0 8px' }}>失败原因</span>,
             dataIndex: 'failMsgs',

+ 3 - 2
src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx

@@ -3,17 +3,18 @@ import { Badge, Space } from "antd"
 import { AdStatus, PromotedObjectType, SpeedMode } from "@/services/launchAdq/enum"
 import TargetingPopover from "../../components/targetingPopover"
 import AdPopover from "../../components/adPopover"
-function tableConfig(callback: (data: any, type: 'log' | 'page', allData?: any) => void): any {
+function tableConfig(callback: (data: any, type: 'log' | 'page' | 'copy', allData?: any) => void): any {
     return [
         {
             title: '操作',
             dataIndex: 'taskName',
             key: 'taskName',
-            width: 60,
+            width: 80,
             align: 'center',
             render: (a: any, b: any) => {
                 return <Space>
                     <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback({ taskId: b.id, campaignName: b.campaignName }, 'log', b) }}>日志</a>
+                    <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback({ taskId: b.id, campaignName: b.campaignName }, 'copy') }}>复制</a>
                 </Space>
             }
         },

+ 1 - 1
src/pages/launchSystemNew/material/cloud/index.tsx

@@ -2,7 +2,7 @@ import FlieBox from '@/components/FileBoxAD'
 import HocError from '@/Hoc/HocError'
 import { QuestionCircleFilled } from '@ant-design/icons'
 import { Button, Space, Tabs, Tooltip } from 'antd'
-import React, { useEffect, useRef, useState } from 'react'
+import React, { useEffect, useState } from 'react'
 import { useModel } from 'umi'
 import AddLandingPage from '../../components/addLandingPage'
 import BathLauCopy from '../../components/bathLauCopy'

+ 46 - 278
src/pages/launchSystemNew/req.ts

@@ -1,6 +1,6 @@
 
 export interface ElementType {
-    elementType: 'TOP_IMAGE' | 'TOP_SLIDER' | 'TOP_VIDEO' | 'IMAGE' | 'SLIDER' | 'VIDEO' | 'TEXT' | 'APP_DOWNLOAD' | 'WEAPP' | 'GH' | 'ENTERPRISE_WX' | 'FLOAT_BUTTON',
+    elementType: 'TOP_IMAGE' | 'TOP_SLIDER' | 'TOP_VIDEO' | 'IMAGE' | 'SLIDER' | 'VIDEO' | 'TEXT' | 'APP_DOWNLOAD' | 'WEAPP' | 'GH' | 'ENTERPRISE_WX' | 'FLOAT_BUTTON' | 'IMAGE_TEXT',
     comptActive?: boolean
 }
 
@@ -40,31 +40,6 @@ export interface WxAutoButton extends Padding, ElementType {
     useIcon: 0 | 1,              // 图标开启关闭 取值 0: 不使用图标, 1: 使用图标. 默认 0
 }
 
-/**跳转链接按钮*/
-export interface LinkButton extends Padding {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    type: "21",
-    name: "跳转链接",
-    btnTitle: string,
-    subType: "0",
-    fontSize: "30",
-    fontColor: string,
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "80",
-    btnBgColorTheme: string,
-    btnBorderColorTheme: string,
-    btnFontType: '0' | '1',
-    btnStyle: "1",
-    cornerRadius: 4,
-    useIcon: '0' | '1',
-    id: "widget_1632641482474_3",
-    mpJumpType: "1",
-    origBtnJumpUrl: string,
-    btnJumpUrl: ""
-}
-
 /**img */
 export interface Img extends ElementType, Padding {
     imageUrl: string,
@@ -89,257 +64,6 @@ export interface TopVideo extends ElementType {
     usedForOuter?: 0 | 1,  //0:顶部素材用于广告外层, 1:顶部素材不用于广告外层. 默认 0. 仅微信平台账号有效. outer_style=1 时选填
 }
 
-/** 图文复合组件标题类型 */
-export interface ImageTextContentTitle {
-    name: "标题",
-    content: string,
-    fontColor: string,
-    fontSize: "32",
-    layoutHeight: "44",
-    layoutWidth: "410",
-    maxLines: "1",
-    showType: "1",
-    type: "1",
-    wxad_IsSubNode: "1"
-}
-/** 图文复合描述标题类型 */
-export interface ImageTextContentDesc {
-    name: string,  // text = 描述 price = 价格
-    content: string, // 内容
-    fontColor: string,  // 字体颜色
-    fontSize: string,  // 字体大小
-    layoutHeight: "40",
-    layoutWidth: "410",
-    paddingTop: "8",
-    type: "1",
-    wxad_IsSubNode: "1",
-    showType?: "1",  // 选择价格才有
-}
-/** 图文复合组件跳转链接类型 */
-export interface ImageTextContentLink {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "2",
-    btnBgColorTheme: string,  // "#07C160"
-    btnBorderColorTheme: string,   // "#FFFFFF"
-    btnFontType: "0",
-    btnHeight: "60",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: string,   // "了解详情"
-    btnType: "0",
-    fontColor: string,    // "#FFFFFF"
-    fontSize: "26",
-    name: string,   // "跳转链接"
-    origBtnJumpUrl: "",
-    layoutWidth: "152",
-    layoutHeight: "60",
-    paddingBottom: "0",
-    paddingLeft: "0",
-    paddingRight: "0",
-    paddingTop: "28",
-    subType: "0",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-/** 图文复合组件关注公众号类型 */
-export interface ImageTextContentGh {
-    widgetTypeV2: string, //"gh",
-    widgetType: string, //"button",
-    type: string, //"21",
-    borderSize: string, //"0",
-    name: string, //"商品关注公众号",
-    btnTitle: string,  // "关注公众号"
-    subType: '17' | '1',
-    fontSize: string, //"26",
-    fontColor: string,  // "#FFFFFF"
-    btnType: string, //"0",
-    btnBgColorTheme: string,  // "#07C160"
-    btnBorderColorTheme: string,  // "#FFFFFF"
-    btnFontType: '0' | '1',
-    btnStyle: string, //"1",
-    cornerRadius: number | string, //4,
-    useIcon: string, //"0",
-    field21_1: {
-        origBtnJumpUrl: string,
-        wxad_guide_group_id: string
-    },
-    id: string,   // "widget_1634288817126_3"
-    layoutWidth: string, //"152",
-    layoutHeight: string, //"60",
-    btnHeight?: string,   //"60",
-    paddingTop?: string,  //"28",
-    paddingBottom?: string, //"0",
-    paddingRight?: string,  // "0"
-    paddingLeft?: string,  // "0"
-    horizontalAlignment?: string  // "1"
-}
-/** 图文复合组件添加商家微信按钮 */
-export interface ImageTextContentWxAuto {
-    widgetTypeV2: string, // "enterprise_wx_auto"
-    widgetType: string, // "button"
-    type: string, // "21"
-    name: string, // "商品添加商家微信"
-    btnTitle: string, //"联系商家",
-    subType: string, //"15"
-    fontSize: string,//"26",
-    fontColor: string,//"#FFFFFF",
-    btnType: string, // "0"
-    borderSize: string, // "0"
-    btnBgColorTheme: string, // "#07C160",
-    btnBorderColorTheme: string, //"#FFFFFF",
-    btnFontType: string, // "0"
-    btnStyle: string, // "1"
-    cornerRadius: number | string, //4,
-    useIcon: string, //"0",
-    mpJumpType: string, //"1",
-    origBtnJumpUrl: string,
-    btnJumpUrl: string, //"",
-    corpid: string,//"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g",
-    groupid: number | null,
-    h5_config_id: string, //"6eeeabc55c3087ea251505ec8560371e",
-    wxapp_config_id: string, //"bee1788a8cbf0bbab69d3bfc5917231e",
-    qrUrl: string,//"https://wework.qpic.cn/wwpic/87591_JQGte4L-Tc21VZW_1645432835/0",
-    needUpdateQrUrl: number, //0,
-    qrExtInfo: string,//"{\"qrType\":1,\"corpid\":\"wp7eXwDAAA4NWRcT0uN05GKQesjmgB0g\",\"groupid\":215982,\"useSet\":0}",
-    serviceType: string, //"1",
-    chatGroupName: string,
-    id: string,
-    layoutWidth: string, //"152",
-    layoutHeight: string, //"60",
-    btnHeight?: string,//"60",
-    paddingTop?: string, //"28",
-    paddingBottom?: string, //"0",
-    paddingRight?: string, //"0",
-    paddingLeft?: string, //"0",
-}
-
-
-/** 图文复合组件单个内容 */
-export interface ImageText1Content {
-    layoutHeight: string,  // "228" 
-    layoutWidth: string,  // "670"
-    type: string,  // "103"
-    subType: string,  // "1"
-    jumpMode: string,  // "btn_jump" "total_jump"
-    layoutItems: {
-        componentItem: [{
-            name: "图片",
-            layoutHeight: "180",
-            layoutWidth: "180",
-            paddingTop: "24",
-            paddingLeft: "24",
-            pureImageUrl: string,
-            type: "41",
-            imageMd5: string,
-            cornerRadius: "4",
-            materialId: "2276131704",
-            wxad_IsSubNode: "1"
-        }, {
-            layoutHeight: "228",
-            layoutWidth: "470",
-            paddingLeft: "24",
-            paddingTop: "24",
-            paddingRight: "40",
-            subType: "0",
-            type: "103",
-            descType: string,  // 文字 text 价格 price
-            wxad_IsSubNode: "1",
-            layoutItems: {
-                componentItem: Array<ImageTextContentTitle | ImageTextContentDesc | ImageTextContentLink | ImageTextContentGh>
-            }
-        }]
-    }
-}
-
-export interface ImageTextOsButtonLink {
-    widgetTypeV2: "link",
-    widgetType: "button",
-    borderSize: "0",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnHeight: "228",
-    btnJumpUrl: "",
-    btnStyle: "1",
-    btnTitle: "",
-    btnType: "0",
-    fontColor: "clear",
-    fontSize: "26",
-    name: string,   // "跳转链接"
-    origBtnJumpUrl: string,
-    layoutWidth: "750",
-    layoutHeight: "228",
-    paddingBottom: "0",
-    paddingLeft: "0",
-    paddingRight: "0",
-    paddingTop: "0",
-    subType: "0",
-    fengyeId: "0",
-    fengyeUrl: "",
-    mpJumpType: "1",
-    type: "21",
-    cornerRadius: "4"
-}
-
-export interface ImageTextOsButtonGh {
-    widgetTypeV2: "gh",
-    widgetType: "button",
-    type: "21",
-    name: "商品关注公众号",
-    btnTitle: "",
-    subType: "17",
-    wxad_guide_group_status: false,
-    fontSize: "26",
-    fontColor: "clear",
-    btnType: "0",
-    borderSize: "0",
-    btnHeight: "228",
-    btnBgColorTheme: "clear",
-    btnBorderColorTheme: "clear",
-    btnFontType: "0",
-    btnStyle: "1",
-    paddingTop: "0",
-    paddingBottom: "0",
-    paddingRight: "0",
-    paddingLeft: "0",
-    cornerRadius: 4,
-    useIcon: "0",
-    field21_1: {
-        origBtnJumpUrl: "",
-        wxad_guide_group_id: ""
-    },
-    id: "widget_1634288817126_3",
-    layoutWidth: "750",
-    layoutHeight: "228"
-}
-
-/** 图文复合组件单个 */
-export interface Shelfnew extends Padding {
-    widgetTypeV2: string,   // 图文复合组件 shelfnew
-    widgetType: string,     // 图文复合组件 shelfnew
-    name: string,    // "图文复合组件"
-    type: string,     // 图文复合组件 单个 "104" 多个 "103"
-    id: string,       // "widget_1634190280938_1"
-    layoutHeight: string, // "228"
-    layoutWidth: string,  // "670"
-    // paddingTop: "20",
-    // paddingBottom: "20",
-    // paddingLeft: "40",
-    // paddingRight: "40",
-    borderColor: string,   // "#e5e5e5"
-    bgColor: string,   // #FFFFFF
-    borderSize: number,  // 1
-    cornerRadius: number,  // 8
-    isChooseJump: number,  // 1
-    layoutItems: {
-        componentItem: Array<ImageText1Content | ImageTextOsButtonLink | ImageTextOsButtonGh>
-    }
-}
-
-
-
 /** 悬浮组件 */
 export interface Floatbutton {
     elementType: 'FLOAT_BUTTON',
@@ -360,4 +84,48 @@ export interface TopSlider extends ElementType {
     width: number,
     height: number,
     activeIndex?: number
-}
+}
+
+
+/** 图文复合组件New 相关 开始======= */
+export interface ITItemGhSpec extends Padding {
+    fastFollow: 0 | 1               // 是否开启一键关注,取值 0: 关闭, 1: 开启. 默认 1
+    btnTitle: string,               // use_icon 为 0 时,长度限制 1-10 ;use_icon 为 1 时,长度限制 1-8 ; 默认:关注公众号
+    fontColor: string,              // 按钮文案颜色 默认#FFFFFF
+    btnBgColorTheme: string,        // 按钮填充色 默认#07C160
+    btnBorderColorTheme: string,    // 边框色,#000000-#FFFFFF,默认#FFFFFF
+    btnFontType: 0 | 1,             // 按钮文案配置 取值 0: 常规, 1: 加粗. 默认 0
+    useIcon: 0 | 1,                 // 是否使用图标 取值 0: 不使用图标, 1: 使用图标. 默认 0
+}
+
+export interface ITItemEnterpriseWxSpec extends Padding {
+    cropId?: string,             // 绑定的企业 id
+    groupId?: number,            // 客服组 id
+    btnTitle: string,            // 按钮文案,
+    fontColor: string,           // 按钮文案颜色,#000000-#FFFFFF,默认#FFFFFF
+    btnBgColorTheme: string,     // 按钮填充色,#000000-#FFFFFF,默认#07C160
+    btnBorderColorTheme: string, // 边框色,#000000-#FFFFFF,默认#FFFFFF
+    btnFontType: 0 | 1,          // 字体粗细 0常用 1加粗
+    useIcon: 0 | 1,              // 图标开启关闭 取值 0: 不使用图标, 1: 使用图标. 默认 0
+}
+
+export interface ImageTextItem {
+    borderColor: string,   // 边框色,#000000-#FFFFFF,默认#e5e5e5
+    titleColor: string,    // 标题颜色,#000000-#FFFFFF. 默认#353535
+    descColor: string,     // 描述颜色,#000000-#FFFFFF. 默认#b2b2b2
+    bgColor: string,       // 背景颜色,#000000-#FFFFFF. 默认#ffffff
+    jumpMode: 'btn_jump' | 'total_jump',  //图文复合组件跳转方式,默认为 btn_jump(按钮跳转)  total_jump(全局跳转)
+    imageUrl: string,      // 图片链接 对标  material_id
+    title: string,         // 标题,长度限制 1-8 ; 字段长度最小 1 字节,长度最大 10 字节
+    desc: string,          // 描述,长度限制 1-10 ;字段长度最小 1 字节,长度最大 10 字节
+    subElemType: 'GH' | 'ENTERPRISE_WX',   // 图文复合组件转化类型,
+    content: ITItemGhSpec | ITItemEnterpriseWxSpec
+}
+
+export interface ImageText extends ElementType, Padding {
+    alignMode: 0 | 1,  // 0 左对齐,1 居中对齐,只有选一行 2 个的时候才生效. 默认值 0
+    imageTextItem: ImageTextItem[]
+}
+
+
+/** 图文复合组件New 相关 结束======= */

+ 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 & 广告名称
+    creativeId?: 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: 'POST',
+        data: params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载单个广告明细表(小时) */
+export async function downLoadDetailApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/up/ad/detail/list`, {
+        method: 'POST',
+        data: params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载单个广告明细表(5分钟) */
+export async function downLoadDetailMinuteApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/up/ad/detail/list/minute`, {
+        method: 'POST',
+        data: params,
+        responseType: 'blob'
+    })
+}
+
+/** 下载消耗速度详情(弹窗) */
+export async function downLoadSpeedApi(params: ListType) {
+    return request(`${api}/tencentMonitor/adExcel/cost/speed/info`, {
+        method: 'POST',
+        data: 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'
+    })
+}

+ 2 - 2
src/services/api.ts

@@ -1,5 +1,5 @@
-export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://test.api.zanxiangwl.com/api'
-// export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://api.zanxiangwl.com'
+// export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://test.api.zanxiangwl.com'
+export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://api.zanxiangwl.com'
 export let dataApi: any = process.env.NODE_ENV === 'development' ? 'dapi' : `http://data.zanxiangnet.com`
 export let wxApi: any = process.env.NODE_ENV === 'development' ? 'wxapi' : `https://report.zanxiangwl.com`
 export let launchApi: any = `http://192.168.7.175:8018`

+ 5 - 1
src/services/launchAdq/createAd.ts

@@ -28,6 +28,10 @@ export interface CreateAdProps {
   bidAmount?: number, // 出价
   expandEnabled?: boolean,  // 自动扩量
   expandTargeting?: string[], // 扩量不可突破定向
+  materialData?: { label: string, name: string, restriction: any, arrayProperty?: any }[]  // 创量模式素材
+  materials?: any[],  // 素材
+  textData?: any[], // 文案所需参数
+  texts?: any[],   // 文案列表
   accountCreateLogs: {
     adAccountId: number, // 媒体账户ID
     userActionSets?: {
@@ -78,7 +82,7 @@ export async function synGoodsApi(data: number[]) {
  * @param data
  * @returns
  */
-export async function getDataSourceApi(data: number[]) {
+export async function getDataSourceApi(data: { accountIds: number[], promotedObjectType?: string }) {
   return request(api + `/adq/userActionSets/allByAccount`, {
     method: 'POST',
     data

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
src/services/launchAdq/enum.ts


+ 11 - 3
src/services/launchAdq/material.ts

@@ -106,13 +106,13 @@ export async function bdSysMediaList(params: { parentId?: any, belongUser: boole
       data: param,
     });
   } else {
-    console.log('param===>',param)
+    console.log('param===>', param)
     return request(api + `/adq/sysWechatComponentPage/list/${belongUser}`, {
       method: 'POST',
       data: param,
     });
   }
-  
+
 }
 /**修改本地素材 */
 export async function bdSysMediaEdit(params: { belongUser: boolean, sysMediaId: number, mediaType: "IMG" | "VIDEO" | 'PAGE' }) {
@@ -153,8 +153,16 @@ export async function bdSysMediaAdd(params: { belongUser: boolean, parentId: num
       data: param,
     });
   }
-
 }
+/**批量新增本地素材 */
+export async function bdSysMediaAddsApi(params: { belongUser: boolean, data: any }) {
+  const { belongUser, ...param } = params
+  return request(api + `/adq//sysMediaImage/addBatch/${belongUser}`, {
+    method: 'POST',
+    data: param.data
+  });
+}
+
 /**删除本地素材 */
 export async function delMedia(params: { sysMediaId: any, mediaType: 'IMG' | 'VIDEO' | 'PAGE' }) {
   if (params.mediaType === 'IMG') {

+ 5 - 1
src/services/launchAdq/taskList.ts

@@ -19,7 +19,11 @@ export async function getTaskListApi(data: TaskListProps) {
     });
 }
 
-
+export async function getTaskDetailsApi(taskId: number) {
+    return request(api + `/adq/adCreateTask/${taskId}`, {
+        method: 'GET'
+    });
+}
 
 /**
  * 任务日志列表

+ 4 - 0
src/services/operating/account.ts

@@ -67,6 +67,10 @@ export async function allOfMember() {
 export async function getAllZhMemBerApi() {
   return request(`${api}/erp/resourceOfUser/allOfMember/10`);
 }
+/** 获取投手 */
+export async function getPicherListApi() {
+  return request(`${api}/erp/user/subUserWithSelf`);
+}
 
 /**编辑公众号分组 */
 export async function editGroup(params: EditGroup) {

+ 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"]
   })
 }
 

+ 23 - 0
src/utils/utils.ts

@@ -114,6 +114,8 @@ export const getTypeKey = (key: string): string => {
       return 'ghSpec'
     case 'ENTERPRISE_WX':
       return 'enterpriseWxSpec'
+    case 'IMAGE_TEXT':
+      return 'imageTextSpec'
     case 'FLOAT_BUTTON':
       return 'floatButtonSpec'
   }
@@ -130,4 +132,25 @@ export const copy = (str: string) => {
   document.execCommand("Copy")
   document.body.removeChild(element);
   message.success(`复制成功:${str}`)
+}
+
+// 数组分组
+export const groupBy = (array: any[], f: (item: any) => any[]) => {
+  const groups = {};
+  array.forEach(function (o) { //注意这里必须是forEach 大写
+    const group = JSON.stringify(f(o));
+    groups[group] = groups[group] || [];
+    groups[group].push(o);
+  });
+  return Object.keys(groups).map(function (group) {
+    return groups[group];
+  });
+}
+
+export const replaceSpecialTxt = (text: string | number | null | undefined) => {
+  if (text) {
+    return text.toString().replace(/[<>]/ig, '')
+  } else {
+    return text
+  }
 }

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio