shenwu 2 роки тому
батько
коміт
6a2358ef0f
28 змінених файлів з 1701 додано та 184 видалено
  1. 6 6
      config/routerConfig.ts
  2. 2 2
      src/pages/launchSystemNew/components/TableData/index.tsx
  3. 23 3
      src/pages/launchSystemNew/components/adModal/index.tsx
  4. 111 0
      src/pages/launchSystemNew/components/creativeModal/index.tsx
  5. 70 0
      src/pages/launchSystemNew/components/creativeModal/tableConfig.tsx
  6. 169 0
      src/pages/launchSystemNew/components/crowdPackModal/index.tsx
  7. 52 0
      src/pages/launchSystemNew/components/crowdPackModal/tableConfig.tsx
  8. 3 3
      src/pages/launchSystemNew/components/dataSourceModal/index.tsx
  9. 2 2
      src/pages/launchSystemNew/components/goodsModal/index.less
  10. 4 12
      src/pages/launchSystemNew/components/goodsModal/index.tsx
  11. 9 1
      src/pages/launchSystemNew/components/targetingModal/index.tsx
  12. 8 0
      src/pages/launchSystemNew/components/targetingPopover/index.less
  13. 73 0
      src/pages/launchSystemNew/components/targetingPopover/index.tsx
  14. 8 0
      src/pages/launchSystemNew/components/targetingTooltip/index.less
  15. 183 0
      src/pages/launchSystemNew/components/targetingTooltip/index.tsx
  16. 85 0
      src/pages/launchSystemNew/launchManage/createAd/adcreativeCol.tsx
  17. 32 0
      src/pages/launchSystemNew/launchManage/createAd/index.less
  18. 241 59
      src/pages/launchSystemNew/launchManage/createAd/index.tsx
  19. 14 4
      src/pages/launchSystemNew/launchManage/createAd/submitModal.tsx
  20. 127 43
      src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx
  21. 98 0
      src/pages/launchSystemNew/launchManage/taskList/index.tsx
  22. 81 0
      src/pages/launchSystemNew/launchManage/taskList/log.tsx
  23. 79 0
      src/pages/launchSystemNew/launchManage/taskList/logTableConfig.tsx
  24. 115 0
      src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx
  25. 1 1
      src/services/api.ts
  26. 58 48
      src/services/launchAdq/createAd.ts
  27. 7 0
      src/services/launchAdq/enum.ts
  28. 40 0
      src/services/launchAdq/taskList.ts

+ 6 - 6
config/routerConfig.ts

@@ -110,12 +110,12 @@ const launchSystem = {
                     access: 'createAd',
                     component: './launchSystemNew/launchManage/createAd',
                 },
-                // {
-                //     path: '/launchSystemNew/launchManage/taskList',
-                //     name: '任务列表',
-                //     access: 'taskList',
-                //     component: './launchSystemNew/launchManage/taskList',
-                // },
+                {
+                    path: '/launchSystemNew/launchManage/taskList',
+                    name: '任务列表',
+                    access: 'taskList',
+                    component: './launchSystemNew/launchManage/taskList',
+                }
             ]
         },
         {

+ 2 - 2
src/pages/launchSystemNew/components/TableData/index.tsx

@@ -41,7 +41,7 @@ interface Prosp {
 }
 
 function TableData(props: Prosp) {
-    const { isZj, scroll, columns, title, dataSource, expandedRowRender, className, leftChild, page = undefined, rowSelection, pageSize = undefined, size = 'small', total = 0, loading = false, onChange, config, configName, ajax, hoverable = true } = props
+    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, hoverable = true } = props
     const { state: userState } = useModel('useOperating.useUser')
     const { isFell } = userState
     const [visible, setVisible] = useState<boolean>(false)
@@ -207,7 +207,7 @@ const Tab = React.memo((props: any) => {
                 total={total}
                 loading={loading}
                 defaultPageSize={20}
-                rowSelection={rowSelection || {}}
+                rowSelection={rowSelection}
                 handelResize={((columns: any) => handelResize(columns))}
             /> : <div className={style.example}>
                 <Spin />

+ 23 - 3
src/pages/launchSystemNew/components/adModal/index.tsx

@@ -1,7 +1,7 @@
 import { FnAjax, useAjax } from "@/Hook/useAjax"
 import { ListData, SysAdgroupsDTO } from "@/services/launchAdq"
 import { getSysAdgroupsList } from "@/services/launchAdq/localAd"
-import { Col, Input, Modal, Row } from "antd"
+import { Col, Input, message, Modal, Row } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import TableData from "../TableData"
 import tableConfig from "./tableConfig"
@@ -13,16 +13,24 @@ interface Props {
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
+    sysAdgroupId?: number
 }
 const AdModal: React.FC<Props> = (props) => {
 
     /**********************/
-    const { visible, onClose, onChange, promotedObjectType } = props
+    const { visible, onClose, onChange, promotedObjectType, sysAdgroupId } = props
     const [oldsearchData, setOldsearchData] = useState<any>(null)
     const sysAdgroupsList: FnAjax<ListData<SysAdgroupsDTO>> = useAjax((params) => getSysAdgroupsList(params))
     const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
     /**********************/
 
+    // 回填
+    useEffect(() => {
+        if (sysAdgroupId) {
+            setSelectedRowKeys([sysAdgroupId])
+        }
+    }, [sysAdgroupId])
+    
     // 初始获取列表
     useEffect(() => {
         getList({ pageSize: 20, pageNum: 1, promotedObjectType })
@@ -41,7 +49,19 @@ const AdModal: React.FC<Props> = (props) => {
 
     // 确定
     const handleOk = () => {
-        onChange && onChange(selectedRowKeys.toString())
+        if (selectedRowKeys?.length) {
+            if (sysAdgroupId) {
+                if (selectedRowKeys?.indexOf(sysAdgroupId) === -1) {
+                    onChange && onChange(selectedRowKeys.toString())
+                } else {
+                    onClose && onClose()
+                }
+            } else {
+                onChange && onChange(selectedRowKeys.toString())
+            }
+        } else {
+            message.error('请选择广告')
+        }
     }
 
     return <Modal title={`选择广告`} bodyStyle={{ padding: 0 }} width={1000} visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>

+ 111 - 0
src/pages/launchSystemNew/components/creativeModal/index.tsx

@@ -0,0 +1,111 @@
+import { FnAjax, useAjax } from "@/Hook/useAjax"
+import { ListData } from "@/services/launchAdq"
+import { getSysAdcreativeList } from "@/services/launchAdq/creative"
+import { PromotedObjectType } from "@/services/launchAdq/enum"
+import { Col, Input, Modal, Row, Select } from "antd"
+import React, { useCallback, useEffect, useState } from "react"
+import TableData from "../TableData"
+import tableConfig from "./tableConfig"
+
+
+interface Props {
+    promotedObjectType: string,
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (data: any) => void,
+    sysAdcreativeId?: number
+}
+/**
+ * 选择创意
+ * @returns 
+ */
+const CreativeModal: React.FC<Props> = (props) => {
+
+    /****************************/
+    const { visible, onClose, onChange, sysAdcreativeId, promotedObjectType } = props
+    const sysAdcreativeList: FnAjax<ListData<any>|any> = useAjax((params) => getSysAdcreativeList(params),{formatResult:true})
+    const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
+    const [oldsearchData, setOldsearchData] = useState<any>(null)
+    /****************************/
+
+    // 回填
+    useEffect(() => {
+        if (sysAdcreativeId) {
+            setSelectedRowKeys([sysAdcreativeId])
+        }
+    }, [sysAdcreativeId])
+
+    // 初始获取列表
+    useEffect(() => {
+        getList({ pageSize: 20, pageNum: 1, promotedObjectType })
+    }, [])
+    // 获取列表
+    const getList = useCallback((arg: { pageSize: number, pageNum: number, adcreativeName?: string, promotedObjectType?: string }) => {
+        Object.keys(arg).forEach(key => {
+            !arg[key] && delete arg[key]
+        })
+        if (JSON.stringify(arg) !== JSON.stringify(oldsearchData)) {
+            setOldsearchData(arg)
+            sysAdcreativeList.run(arg)
+        }
+    }, [oldsearchData])
+
+    const handleOk = () => {
+        onChange && onChange(selectedRowKeys.toString())
+    }
+    
+    return <Modal title={`选择创意`} bodyStyle={{ padding: 0 }} width={1000} visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
+        <TableData
+            hoverable={false}
+            columns={tableConfig}
+            ajax={sysAdcreativeList}
+            dataSource={sysAdcreativeList?.data?.data?.records}
+            loading={sysAdcreativeList?.loading}
+            scroll={{ y: 400 }}
+            total={sysAdcreativeList?.data?.data?.total}
+            page={sysAdcreativeList?.data?.data?.current}
+            pageSize={sysAdcreativeList?.data?.data?.size}
+            rowSelection={{
+                type: 'radio',
+                selectedRowKeys: selectedRowKeys,
+                onChange: (selectedRowKeys: React.Key[]) => {
+                    setSelectedRowKeys(selectedRowKeys)
+                }
+            }}
+            leftChild={<>
+                <Row gutter={[10, 10]}>
+                    <Col>
+                        <Input
+                            placeholder='创意名称'
+                            allowClear
+                            onBlur={(e) => {
+                                let value = e.target.value
+                                getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                            }}
+                            onKeyDownCapture={(e: any) => {
+                                let key = e.key
+                                if (key === 'Enter') {
+                                    let value = e.target.value
+                                    getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                                }
+                            }}
+                            onChange={(e) => {
+                                let value = e.target.value
+                                if (!value) {
+                                    getList({ pageNum: 1, pageSize: 20, adcreativeName: value })
+                                }
+                            }}
+                        />
+                    </Col>
+                </Row>
+            </>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                getList({ pageNum: current, pageSize })
+            }}
+        />
+    </Modal>
+}
+
+export default React.memo(CreativeModal)

+ 70 - 0
src/pages/launchSystemNew/components/creativeModal/tableConfig.tsx

@@ -0,0 +1,70 @@
+import { PageTypeEnum, PromotedObjectType } from '@/services/launchAdq/enum'
+function tableConfig():any{
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 50
+        },
+        {
+            title: '创意名称',
+            dataIndex: 'adcreativeName',
+            key: 'adcreativeName',
+            width: 160,
+            ellipsis: true
+        },
+        {
+            title: '推广目标',
+            dataIndex: 'promotedObjectType',
+            key: 'promotedObjectType',
+            align: 'center',
+            render: (a: string | number) => {
+                return PromotedObjectType[a]
+            }
+        },
+        {
+            title: '落地页类型',
+            dataIndex: 'pageType',
+            key: 'pageType',
+            align: 'center',
+            width: 120,
+            render: (a: string) => {
+                return PageTypeEnum[a]
+            }
+        },
+        {
+            title: '创意形式ID',
+            dataIndex: 'adcreativeTemplateId',
+            key: 'adcreativeTemplateId',
+            align: 'center',
+        },
+        {
+            title: '数据展示',
+            dataIndex: 'conversionDataType',
+            key: 'conversionDataType',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return b.conversionDataType || b.conversionTargetType ? '开' : '关'
+            }
+        },
+        {
+            title: '行动开关',
+            dataIndex: 'linkNameType',
+            key: 'linkNameType',
+            align: 'center',
+            render: (a: any, b: any) => {
+                return b.linkNameType || b.linkPageType ? '开' : '关'
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140
+        }
+    ]
+}
+export default tableConfig

+ 169 - 0
src/pages/launchSystemNew/components/crowdPackModal/index.tsx

@@ -0,0 +1,169 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import { getCrowdPackApi, sysCrowdPackApi } from "@/services/launchAdq/createAd"
+import { CheckOutlined, SyncOutlined } from "@ant-design/icons"
+import { Button, Input, Modal, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../goodsModal/index.less'
+import columns from "./tableConfig"
+
+
+const crowdpackData = [
+    {
+        id: 1,
+        name: '定向用户群'
+    },
+    {
+        id: 2,
+        name: '排除用户群'
+    }
+]
+
+/**
+ * 获取人群包列表
+ * @returns 
+ */
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (data: any) => void,
+    data: any
+}
+const CrowdPackModal: React.FC<Props> = (props) => {
+
+    /*****************/
+    const { visible, onClose, data: data1, onChange } = props
+    const [data, setData] = useState<any>(data1)
+    const [tableData, setTableData] = useState<any[]>([])//table数据
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择账户
+    const [selectCp, setSelectCp] = useState<number>(1)   // 选择自定义
+    const [name, setName] = useState<string>('')
+
+    const getCrowdPack = useAjax((params) => getCrowdPackApi(params))
+    const sysCrowdPack = useAjax((params) => sysCrowdPackApi(params))
+    /*****************/
+
+    useEffect(() => {
+        // customAudienceList?: any, excludedCustomAudienceList?: any
+        if (data?.length > 0) {
+            getList([data[selectAdz - 1].adAccountId])
+        } else {
+            setTableData([])
+        }
+    }, [selectAdz])
+
+    const getKey = (index: number, reverse?: boolean) => {
+        if (reverse) {
+            if (index === 1) return 'excludedCustomAudienceList'
+            return 'customAudienceList'
+        } else {
+            if (index === 1) return 'customAudienceList'
+            return 'excludedCustomAudienceList'
+        }
+    }
+
+    // 获取人群包列表
+    const getList = (params: number[]) => {
+        getCrowdPack.run(params).then(res => {
+            console.log('res===>', res)
+            if (res && Object.keys(res)?.indexOf(params[0].toString()) !== -1) {
+                setTableData(res[params[0]]?.map((item: { audienceId: string }) => ({ ...item, id: item.audienceId })))
+            } else {
+                setTableData([])
+            }
+        })
+    }
+
+    const handleOk = () => {
+        onChange && onChange(data)
+    }
+
+    // 同步人群包
+    const synGoodsList = () => {
+        sysCrowdPack.run(data?.map((item: { id: number }) => item?.id)).then(res => {
+            getList([data[selectAdz - 1].adAccountId])
+        })
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectAdz = (value: number, item: any) => {
+        if (value === selectAdz) {
+            return
+        }
+        setSelectAdz(value)
+    }
+
+    const handleSelectCp = (value: number) => {
+        if (value === selectCp) {
+            return
+        }
+        setSelectCp(value)
+    }
+
+    /** 表格选折 */
+    const onChangeTable = (selectedRowKeys: React.Key[], selectedRows: any) => {
+        let newData = JSON.parse(JSON.stringify(data))
+        newData[selectAdz - 1][getKey(selectCp)] = selectedRows
+        setData([...newData])
+    }
+
+    return <Modal
+        title={<Space>
+            <span>人群包</span>
+            <Button size="small" onClick={() => { synGoodsList() }} type="link" loading={sysCrowdPack.loading}>同步人群包</Button>
+        </Space>}
+        visible={visible}
+        onCancel={() => { onClose && onClose() }}
+        onOk={handleOk}
+        width={1100}
+        className={style.SelectPackage}
+        bodyStyle={{ padding: '0 10px 0 10px' }}
+    >
+        <div className={style.content}>
+            <div className={style.left} style={{ width: 140 }}>
+                <h4 className={style.title}>人或目标场景</h4>
+                {crowdpackData.map((item: { id: number, name: string }, index: number) => (
+                    <div key={index} onClick={() => { handleSelectCp(index + 1) }} className={`${style.accItem} ${selectCp === index + 1 && style.select} `}>
+                        {item?.name}
+                        {/* {orientPackage[selectAdz - 1]?.goods?.key === item?.catalogId && orientPackage[selectAdz - 1].goods?.data?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />} */}
+                    </div>))
+                }
+            </div>
+            <div className={style.left}>
+                <h4 className={style.title}>媒体账户</h4>
+                {data?.map((item: { adAccountId: number, id: number }, index: number) => (
+                    <div key={index} onClick={() => { handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
+                        {item?.adAccountId}
+                        {data[index][getKey(selectCp)]?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                    </div>))}
+            </div>
+            <div className={style.right}>
+                <Space style={{ marginBottom: 10 }}>
+                    <Input placeholder="请输入用户群名称" allowClear value={name} onChange={(e) => setName(e.target.value)} />
+                    <Button icon={<SyncOutlined />} type='link' loading={getCrowdPack?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
+                </Space>
+                <Tables
+                    columns={columns()}
+                    dataSource={tableData?.filter((item: { name: string }) => name ? item.name.indexOf(name) !== -1 : true)}
+                    size="small"
+                    loading={getCrowdPack?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                    defaultPageSize={100}
+                    rowSelection={{
+                        type: 'checkbox',
+                        selectedRowKeys: data[selectAdz - 1][getKey(selectCp)]?.map((item: any) => item?.id?.toString()),
+                        onChange: onChangeTable,
+                        getCheckboxProps: (record: any) => ({
+                            disabled: data[selectAdz - 1][getKey(selectCp, true)]?.map((item: any) => item?.id)?.includes(record.id),
+                            id: record.id,
+                        })
+                    }}
+                />
+            </div>
+        </div>
+    </Modal>
+}
+
+
+export default React.memo(CrowdPackModal)

+ 52 - 0
src/pages/launchSystemNew/components/crowdPackModal/tableConfig.tsx

@@ -0,0 +1,52 @@
+import { CrowdSourceEnum } from "@/services/launchAdq/enum"
+import React from "react"
+let columns = () => [
+    {
+        title: '用户群名称',
+        dataIndex: 'name',
+        key: 'name',
+        width: 260,
+        ellipsis: true,
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: 'ID',
+        dataIndex: 'audienceId',
+        key: 'audienceId',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: '来源',
+        dataIndex: 'source',
+        key: 'source',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{CrowdSourceEnum[a]}</span>
+        }
+    },
+    {
+        title: '覆盖数量',
+        dataIndex: 'userCount',
+        key: 'userCount',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+    {
+        title: '状态',
+        dataIndex: 'status',
+        key: 'status',
+        align: 'center',
+        render: (a: any, b: any) => {
+            return <span style={{ fontSize: "12px" }}>{a}</span>
+        }
+    },
+]
+
+export default columns

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

@@ -79,7 +79,7 @@ const DataSourceModal: React.FC<Props> = (props) => {
     return <Modal
         title={<Space>
             <span>数据源</span>
-            <Button size="small" onClick={() => { synDataSourceList() }} loading={sysDataSource?.loading}>同步数据源</Button>
+            <Button size="small" onClick={() => { synDataSourceList() }} type='link' loading={sysDataSource?.loading}>同步数据源</Button>
         </Space>}
         visible={visible}
         onCancel={() => { onClose && onClose() }}
@@ -90,7 +90,7 @@ const DataSourceModal: React.FC<Props> = (props) => {
     >
         <div className={style.content}>
             <div className={style.left}>
-                <h4 className={style.title}>广告主账户</h4>
+                <h4 className={style.title}>媒体账户</h4>
                 {data?.map((item: { adAccountId: number, id: number }, index: number) => (
                     <div key={index} onClick={() => { handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
                         {item?.adAccountId}
@@ -98,7 +98,7 @@ const DataSourceModal: React.FC<Props> = (props) => {
                     </div>))}
             </div>
             <div className={style.right}>
-                <Space style={{ marginBottom: 10 }}>
+                <Space style={{ marginBottom: 10 }} align="end">
                     <Button icon={<SyncOutlined />} type='link' loading={getDataSource?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
                 </Space>
                 <Tables

+ 2 - 2
src/pages/launchSystemNew/components/goodsModal/index.less

@@ -5,7 +5,7 @@
         justify-content: flex-start;
         min-height: 200px;
         .left{
-            width: 200px;
+            width: 150px;
             padding-top: 10px;
             border-right: 1px solid #f0f0f0;
             box-sizing: border-box;
@@ -43,7 +43,7 @@
         .right{
             padding: 10px;
             box-sizing: border-box;
-            width: calc(100% - 200px);
+            width: calc(100% - 150px);
         }
     }
 

+ 4 - 12
src/pages/launchSystemNew/components/goodsModal/index.tsx

@@ -78,7 +78,7 @@ const GoodsModal: React.FC<Props> = (props) => {
     return <Modal
         title={<Space>
             <span>商品库</span>
-            <Button size="small" onClick={() => { synGoodsList() }} loading={synGoods?.loading}>同步商品库</Button>
+            <Button size="small" onClick={() => { synGoodsList() }} type="link" loading={synGoods?.loading}>同步商品库</Button>
         </Space>}
         visible={visible}
         onCancel={() => { onClose && onClose() }}
@@ -89,24 +89,15 @@ const GoodsModal: React.FC<Props> = (props) => {
     >
         <div className={style.content}>
             <div className={style.left}>
-                <h4 className={style.title}>广告主账户</h4>
+                <h4 className={style.title}>媒体账户</h4>
                 {data?.map((item: { adAccountId: number, id: number }, index: number) => (
                     <div key={index} onClick={() => { handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
                         {item?.adAccountId}
                         {data[index].productList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
                     </div>))}
             </div>
-            {/* <div className={style.left}>
-                <h4 className={style.title}>商品库</h4>
-                {goodsData?.map((item: { name: string, goodsList: any[], catalogId: number }, index: number) => (
-                    <div key={index} onClick={() => { handleGoods(index + 1, { ...queryFormDetail, godsId: goodsData[index].catalogId }) }} className={`${style.accItem} ${selectGoods === index + 1 && style.select} `}>
-                        {item?.name}
-                        {orientPackage[selectAdz - 1]?.goods?.key === item?.catalogId && orientPackage[selectAdz - 1].goods?.data?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
-                    </div>))
-                }
-            </div> */}
             <div className={style.right}>
-                <Space style={{ marginBottom: 10 }}>
+                <Space style={{ marginBottom: 10 }} align="end">
                     <Button icon={<SyncOutlined />} type='link' loading={getGoods?.loading} onClick={() => { getList([data[selectAdz - 1].adAccountId]) }}></Button>
                 </Space>
                 <Tables
@@ -116,6 +107,7 @@ const GoodsModal: React.FC<Props> = (props) => {
                     loading={getGoods?.loading}
                     scroll={{ y: 300 }}
                     bordered
+                    defaultPageSize={100}
                     rowSelection={{
                         type: 'radio',
                         selectedRowKeys: data[selectAdz - 1]?.productList?.map((item: any) => item?.id?.toString()),

+ 9 - 1
src/pages/launchSystemNew/components/targetingModal/index.tsx

@@ -12,17 +12,25 @@ interface Props {
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void,
+    sysTargetingId?: number
 }
 const TargetingModal: React.FC<Props> = (props) => {
 
     /*****************************/
-    const { visible, onClose, onChange } = props
+    const { visible, onClose, onChange, sysTargetingId } = props
     const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
     const [oldsearchData, setOldsearchData] = useState<any>(null)
 
     const list: FnAjax<ListData<any>> = useAjax((params) => getsysTargetingList(params))
     /*****************************/
 
+    // 回填数据
+    useEffect(() => {
+        if (sysTargetingId) {
+            setSelectedRowKeys([sysTargetingId])
+        }
+    }, [sysTargetingId])
+
     // 初始获取列表
     useEffect(() => {
         getList({ pageSize: 20, pageNum: 1 })

+ 8 - 0
src/pages/launchSystemNew/components/targetingPopover/index.less

@@ -0,0 +1,8 @@
+
+
+.popover {
+    width: 350px;
+    max-height: 150px;
+    overflow: hidden;
+    overflow-y: auto;
+}

+ 73 - 0
src/pages/launchSystemNew/components/targetingPopover/index.tsx

@@ -0,0 +1,73 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getTagsList } from "@/services/launchAdq/global"
+import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
+import { Popover, Spin } from "antd"
+import React, { useState } from "react"
+import TargetingTooltip from "../targetingTooltip"
+import style from './index.less'
+
+
+/**
+ * 表格查看信息
+ * @returns 
+ */
+interface Props {
+    id: number
+}
+const TargetingPopover: React.FC<Props> = (props) => {
+
+    /***************************/
+    const { id } = props
+    const [visible, setVisible] = useState<boolean>(false)
+    const [geoLocationList, setGeoLocationList] = useState<any>({}) // 所有地域列表
+    const [modelList, setModelList] = useState<any>({})  // 所有品牌手机
+
+    const tagsList_REGION = useAjax((params) => getTagsList(params))
+    const tagsList_MODEL = useAjax((params) => getTagsList(params))
+    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
+    /***************************/
+
+    const handleVisibleChange = (newVisible: boolean) => {
+        setVisible(newVisible)
+        if (id && newVisible) {
+            getsysTargeting.run(id)
+            tagsList_REGION.run({ type: 'REGION' }).then(res => {
+                if (res) {
+                    setGeoLocationList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                        prev[cur.id] = cur
+                        return prev
+                    }, {}))
+                }
+            })
+            tagsList_MODEL.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
+                if (res) {
+                    setModelList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                        prev[cur.id] = cur
+                        return prev
+                    }, {}))
+                }
+            })
+        }
+    }
+
+    return <Popover
+        content={<Spin spinning={getsysTargeting.loading || tagsList_MODEL.loading || tagsList_REGION.loading}>
+            <div className={style.popover}>
+                {getsysTargeting?.data && <>
+                    <div>定向名称: <span>{getsysTargeting?.data?.targetingName}</span></div>
+                    <div>定向描述: <span>{getsysTargeting?.data?.description}</span></div>
+                    <TargetingTooltip data={getsysTargeting?.data} geoLocationList={geoLocationList} modelList={modelList} />
+                </>}
+            </div>
+        </Spin>}
+        // title="定向"
+        trigger="click"
+        placement="left"
+        visible={visible}
+        onVisibleChange={handleVisibleChange}
+    >
+        <a style={{ color: '#1890ff', fontSize: 12 }}>查看定向</a>
+    </Popover>
+}
+
+export default React.memo(TargetingPopover)

+ 8 - 0
src/pages/launchSystemNew/components/targetingTooltip/index.less

@@ -0,0 +1,8 @@
+.targetingTooltip {
+    >div {
+        font-size: 12px;
+        >span {
+            color: #3085ff;
+        }
+    }
+}

+ 183 - 0
src/pages/launchSystemNew/components/targetingTooltip/index.tsx

@@ -0,0 +1,183 @@
+import { DevicePriceEnum, EducationEnum, ExcludedDimensionEnum, MaritalStatusEnum, NetworkEnum, OptimizationGoalEnum, UserOsEnum, WechatAdBehaviorEnum } from "@/services/launchAdq/enum"
+import React, { useEffect, useState } from "react"
+import './index.less'
+
+const targetingData = [
+    { key: 'geoLocation', name: '地域' },
+    { key: 'age', name: '年龄' },
+    { key: 'gender', name: '性别' },
+    { key: 'education', name: '学历' },
+    { key: 'maritalStatus', name: '婚恋' },
+    // { key: 'customAudience', name: '定向人群' },
+    { key: 'deviceBrandModel', name: '品牌型号' },
+    { key: 'wechatAdBehavior', name: '微信再营销' },
+    { key: 'networkType', name: '联网方式' },
+    { key: 'devicePrice', name: '设备价格' },
+    { key: 'userOs', name: '手机系统' },
+    { key: 'excludedConvertedAudience', name: '排除已转化用户' }
+]
+interface Props {
+    data: any,
+    geoLocationList: any // 所有地域
+    modelList: any  // 所有品牌手机
+}
+interface ContentProps {
+    unlimited?: string,         // 不限
+    geoLocation?: any[],        // 地域
+    age?: string,               // 年龄
+    gender?: string,            // 性别
+    education?: string[]        // 学历
+    maritalStatus?: string[],   // 婚恋
+    deviceBrandModel?: {
+        excludedList?: string[], // 品牌型号
+        includedList?: string[]  // 排除品牌型号
+    },
+    wechatAdBehavior?: {
+        actions?: string[],      // 再营销
+        excludedActions?: string[]// 排除营销
+    },
+    networkType?: string[]      // 联网方式
+    devicePrice?: string[]      // 手机价格
+    userOs?: string[]           // 手机系统
+    excludedConvertedAudience?: {
+        conversionBehaviorList: string[],
+        excludedDimension: string
+    }
+}
+/**
+ * 定向处理展示
+ * @returns 
+ */
+const TargetingTooltip: React.FC<Props> = (props) => {
+
+    /**********************/
+    const { data, geoLocationList, modelList } = props
+    const [content, setContent] = useState<ContentProps>({})
+    /**********************/
+
+    useEffect(() => {
+        if (data && geoLocationList) {
+            targetingData.forEach(item => {
+                if (Object.keys(data?.targeting)?.includes(item.key)) {
+
+                }
+            })
+            let newConten: ContentProps = {}
+            newConten.unlimited = targetingData.filter((item: any) => !Object.keys(data?.targeting).includes(item.key))?.map((item: any) => item?.name)?.toString()
+            Object.keys(data?.targeting)?.forEach((item: any) => {
+                switch (item) {
+                    case 'geoLocation': // 地域
+                        if (data?.targeting[item]?.regions && data?.targeting[item]?.regions?.length > 0) {
+                            newConten.geoLocation = data?.targeting[item]?.regions?.map((item: any) => geoLocationList[item]?.name)
+                        }
+                        break
+                    case 'age':
+                        let newAge = data?.targeting[item][0]
+                        newConten.age = `${newAge.min}至${newAge.max > 65 ? '66岁及以上' : newAge.max + '岁'}`
+                        break
+                    case 'gender':
+                        newConten.gender = data?.targeting[item][0]
+                        break
+                    case 'education':
+                        newConten.education = data?.targeting[item]
+                        break
+                    case 'maritalStatus':
+                        newConten.maritalStatus = data?.targeting[item]
+                        break
+                    case 'deviceBrandModel': // 品牌型号
+                        let newData = data?.targeting[item]
+                        let deviceBrandModel: { excludedList?: string[], includedList?: string[] } = {}
+                        if (newData?.excludedList) {
+                            deviceBrandModel.excludedList = newData?.excludedList?.map((key: any) => modelList[key]?.name)
+                        }
+                        if (newData?.includedList) {  // 排除
+                            deviceBrandModel.includedList = newData?.includedList?.map((key: any) => modelList[key]?.name)
+                        }
+                        newConten.deviceBrandModel = deviceBrandModel
+                        break
+                    case 'wechatAdBehavior': // 微信再营销
+                        let wechatAdBehaviorData = data?.targeting[item]
+                        let wechatAdBehavior: {
+                            actions?: string[],      // 再营销
+                            excludedActions?: string[]// 排除营销
+                        } = {}
+                        if (wechatAdBehaviorData?.actions) {
+                            wechatAdBehavior.actions = wechatAdBehaviorData?.actions
+                        }
+                        if (wechatAdBehaviorData?.excludedActions) {  // 排除
+                            wechatAdBehavior.excludedActions = wechatAdBehaviorData?.excludedActions
+                        }
+                        newConten.wechatAdBehavior = wechatAdBehavior
+                        break
+                    case 'networkType':
+                        newConten.networkType = data?.targeting[item]
+                        break
+                    case 'devicePrice':
+                        newConten.devicePrice = data?.targeting[item]
+                        break
+                    case 'userOs':
+                        newConten.userOs = data?.targeting[item]
+                        break
+                    case 'excludedConvertedAudience':
+                        newConten.excludedConvertedAudience = data?.targeting[item]
+                        break
+                }
+                setContent(newConten)
+            })
+            
+        }
+    }, [data, geoLocationList])
+
+
+
+    return <div className='targetingTooltip'>
+        {content?.geoLocation && <div>
+            地域:<span>{content?.geoLocation?.toString()}</span>
+        </div>}
+        {content?.age && <div>
+            年龄:<span>{content?.age}</span>
+        </div>}
+        {content?.gender && <div>
+            性别:<span>{content?.gender === 'MALE' ? '男' : '女'}</span>
+        </div>}
+        {(content?.education && content?.education?.length > 0) && <div>
+            学历:<span>{content?.education?.map((key: string) => EducationEnum[key]).toString()}</span>
+        </div>}
+        {(content?.networkType && content?.networkType?.length > 0) && <div>
+            联网方式:<span>{content?.networkType?.map((key: string) => NetworkEnum[key]).toString()}</span>
+        </div>}
+        {(content?.devicePrice && content?.devicePrice?.length > 0) && <div>
+            手机价格:<span>{content?.devicePrice?.map((key: string) => DevicePriceEnum[key]).toString()}</span>
+        </div>}
+        {(content?.userOs && content?.userOs?.length > 0) && <div>
+            手机系统:<span>{content?.userOs?.map((key: string) => UserOsEnum[key]).toString()}</span>
+        </div>}
+        {(content?.maritalStatus && content?.maritalStatus?.length > 0) && <div>
+            婚恋:<span>{content?.maritalStatus?.map((key: string) => MaritalStatusEnum[key]).toString()}</span>
+        </div>}
+        {content?.deviceBrandModel && <>
+            {(content?.deviceBrandModel?.includedList && content?.deviceBrandModel?.includedList?.length > 0) && <div>
+                排除品牌型号:<span>{content?.deviceBrandModel?.includedList?.toString()}</span>
+            </div>}
+            {(content?.deviceBrandModel?.excludedList && content?.deviceBrandModel?.excludedList?.length > 0) && <div>
+                品牌型号:<span>{content?.deviceBrandModel?.excludedList?.toString()}</span>
+            </div>}
+        </>}
+        {content?.wechatAdBehavior && <>
+            {(content?.wechatAdBehavior?.actions && content?.wechatAdBehavior?.actions?.length > 0) && <div>
+                再营销:<span>{content?.wechatAdBehavior?.actions?.map((key: string) => WechatAdBehaviorEnum[key]).toString()}</span>
+            </div>}
+            {(content?.wechatAdBehavior?.excludedActions && content?.wechatAdBehavior?.excludedActions?.length > 0) && <div>
+                排除营销:<span>{content?.wechatAdBehavior?.excludedActions?.map((key: string) => WechatAdBehaviorEnum[key]).toString()}</span>
+            </div>}
+        </>}
+        {content?.excludedConvertedAudience && <div>
+            排除已转化用户:<span>{ExcludedDimensionEnum[content?.excludedConvertedAudience?.excludedDimension]}{`(自定义转化行为:${OptimizationGoalEnum[content?.excludedConvertedAudience?.conversionBehaviorList[0]]})`}</span>
+        </div>}
+        {content?.unlimited && <div>
+            不限:<span>{!content?.geoLocation && '地域,'}{content?.unlimited}</span>
+        </div>}
+    </div>
+}
+
+export default React.memo(TargetingTooltip)

+ 85 - 0
src/pages/launchSystemNew/launchManage/createAd/adcreativeCol.tsx

@@ -0,0 +1,85 @@
+import { useAjax } from "@/Hook/useAjax"
+import { PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import { get_adcreative_template_list } from "@/services/launchAdq/global"
+import React, { useEffect, useState } from "react"
+import { outAdcreativeTemplateIdFun } from "../localAd/adenum"
+import { Image, Space, Spin } from "antd"
+
+/**
+ * 创意详情
+ */
+interface Props {
+    data: any,
+    onChange?: (value: string) => void
+}
+const AdcreativeCol: React.FC<Props> = (props) => {
+
+    /***************************/
+    const { data, onChange } = props
+    const { adcreativeName, promotedObjectType, siteSet, adcreativeTemplateId, adcreativeElements } = data
+    const [adcreative_template_list, set_adcreative_template_list] = useState<any[]>([])
+
+    const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
+    /***************************/
+
+    /** 获取创意形式 */
+    useEffect(() => {
+        if (siteSet?.length > 0 && promotedObjectType && !(adcreative_template_list.length > 0)) {
+            getAdcreativeTemplateList.run({
+                siteSet,
+                promotedObjectType,
+                campaignType: 'CAMPAIGN_TYPE_NORMAL',
+            }).then(res => {
+                let newArr: any = []
+                // 过滤掉相同的和即将下线的
+                Object.values(res)?.forEach((arr: any) => {
+                    Array.isArray(arr) && arr?.forEach((item: any) => {
+                        if (newArr.length > 0) {
+                            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 {
+                            if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
+                                newArr.push(item)
+                            }
+                        }
+                    })
+                })
+                set_adcreative_template_list(newArr)
+            })
+        }
+    }, [siteSet, promotedObjectType])
+
+    useEffect(() => {
+        if (adcreative_template_list?.length > 0 && adcreativeTemplateId) {
+            onChange && onChange(adcreative_template_list?.find((item: any) => item?.adcreativeTemplateId === adcreativeTemplateId)?.adcreativeTemplateAppellation || '')
+        }
+    }, [adcreative_template_list, adcreativeTemplateId])
+
+    return <Spin size="small" spinning={getAdcreativeTemplateList?.loading}>
+        <div>创意名称: <span>{adcreativeName}</span></div>
+        <div>推广目标: <span>{PromotedObjectType[promotedObjectType]}</span></div>
+        <div>广告版位: <span>{siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span></div>
+        <div>创意形式: <span>{adcreative_template_list?.find((item: any) => item?.adcreativeTemplateId === adcreativeTemplateId)?.adcreativeTemplateAppellation || ''}</span></div>
+        {adcreativeElements?.description && <div>创意文案: <span>{adcreativeElements?.description}</span></div>}
+        {adcreativeElements?.title && <div>文案: <span>{adcreativeElements?.title}</span></div>}
+        {adcreativeElements?.imageUrl && <div style={{ display: 'flex', alignItems: 'flex-start' }}>创意素材: <Image width={80} src={adcreativeElements?.imageUrl} style={{ borderRadius: 8, overflow: 'hidden', marginLeft: 5 }} /></div>}
+        {adcreativeElements?.imageUrlList && <div style={{ display: 'flex', alignItems: 'flex-start', flexWrap: 'wrap' }}><span style={{ marginRight: 4 }}>创意素材:</span> <Image.PreviewGroup>
+            <Space wrap>
+                {adcreativeElements?.imageUrlList?.map((url: string, index: number) => <Image width={50} src={url} style={{ borderRadius: 4 }} key={'TOP_SLIDER' + index} />)}
+            </Space>
+        </Image.PreviewGroup></div>}
+        {adcreativeElements?.shortVideoStruct?.shortVideo1Url && <div style={{ display: 'flex', alignItems: 'flex-start' }}><span style={{ marginRight: 4 }}>创意素材:</span> <video src={adcreativeElements?.shortVideoStruct?.shortVideo1Url} width={130} controls></video></div>}
+        {adcreativeElements?.videoUrl && <div style={{ display: 'flex', alignItems: 'flex-start' }}><span style={{ marginRight: 4 }}>创意素材:</span> <video src={adcreativeElements?.videoUrl} width={130} controls></video></div>}
+    </Spin>
+}
+
+export default React.memo(AdcreativeCol)

+ 32 - 0
src/pages/launchSystemNew/launchManage/createAd/index.less

@@ -93,6 +93,10 @@
           .centerContent {
             width: 100%;
             min-height: 200px;
+            font-size: 12px;
+            // span {
+            //   color: #3085ff;
+            // }
           }
 
           .acc {
@@ -164,3 +168,31 @@
     justify-content: flex-end;
   }
 }
+.popoverContent {
+  border: 1px solid #dcdee2;
+  padding: 5px;
+  border-radius: 4px;
+  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;
+  overflow: hidden;
+  overflow-y: scroll;
+}
+
+.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;
+}

+ 241 - 59
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -1,21 +1,27 @@
 import Tables from "@/components/Tables"
 import { useAjax } from "@/Hook/useAjax"
-import { CreateAdProps } from "@/services/launchAdq/createAd"
+import { createAdBatchApi, CreateAdProps } from "@/services/launchAdq/createAd"
+import { getSysAdcreativeInfo } from "@/services/launchAdq/creative"
 import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } 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 } from "antd"
+import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Popover } from "antd"
 import React, { useEffect, useState } from "react"
 import { useModel } from "umi"
 import AdModal from "../../components/adModal"
+import CreativeModal from "../../components/creativeModal"
+import CrowdPackModal from "../../components/crowdPackModal"
 import DataSourceModal from "../../components/dataSourceModal"
 import GoodsModal from "../../components/goodsModal"
 import IdModal from "../../components/idModal"
 import LookLanding from "../../components/lookLanding"
 import SelectCloud from "../../components/selectCloud"
 import TargetingModal from "../../components/targetingModal"
+import TargetingTooltip from "../../components/targetingTooltip"
 import { WxAutoButton } from "../../req"
+import AdcreativeCol from "./adcreativeCol"
 import style from './index.less'
 import Selector from "./selector"
 import SubmitModal from "./submitModal"
@@ -30,31 +36,72 @@ const CreateAd: React.FC = () => {
         campaignType: 'CAMPAIGN_TYPE_NORMAL', // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
         promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT', // 推广目标类型
         speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
-        sysAdgroupsId: undefined,  // 广告组内容
+        sysAdgroupId: undefined,  // 广告组内容
         sysTargetingId: undefined,  // 定向包 id
         adName: undefined,  // 广告名称
         configuredStatus: 'AD_STATUS_SUSPEND',  // 广告状态
         sysAdcreativeId: undefined, // 创意ID
         sysPageId: undefined, // 落地页Id
     })
-    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: number, productList?: any, conversionList?: any }[]>([])  // 账户
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: number, productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any }[]>([])  // 账户
 
     const [adVisible, setAdVisible] = useState<boolean>(false) // 选择广告弹窗控制
     const [dxVisible, setDxVisible] = useState<boolean>(false) // 选择定向弹窗控制
+    const [creativeVisible, setCreativeVisible] = useState<boolean>(false) // 选择创意弹窗控制
     const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
     const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
     const [idVisible, setIdVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [selectImgVisible, setSelectImgVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [lookVisible, setLookVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
-    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
-    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
+    const [cpVisible, setCpVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
     const [wxButtonList, setWxButtonList] = useState<WxAutoButton[]>([])
     const [tableData, setTableData] = useState<any[]>([])   // 预览表格
     const [tableSelect, setTableSelect] = useState<any[]>([])
+    const [geoLocationList, setGeoLocationList] = useState<any>({}) // 所有地域列表
+    const [modelList, setModelList] = useState<any>({})  // 所有品牌手机
+    const [adcreativeTemplateAppellation, setAdcreativeTemplateAppellation] = useState<string>('')  // 创意形式
     const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
+
+
+    const tagsList_REGION = useAjax((params) => getTagsList(params))
+    const tagsList_MODEL = useAjax((params) => getTagsList(params))
+    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
+    const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
+    const getSysAdcreative = useAjax((params) => getSysAdcreativeInfo(params))
+    const createAdBatch = useAjax((params) => createAdBatchApi(params))
     /*************************/
 
+    /**数据回填 */
+    useEffect(() => {
+        let adqAdData = localStorage.getItem('ADQAD')
+        if (adqAdData) {
+            const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
+            setQueryForm({ ...queryForm })
+            setAccountCreateLogs(accountCreateLogs)
+        }
+    }, [])
+
+    // 设置地域
+    useEffect(() => {
+        tagsList_REGION.run({ type: 'REGION' }).then(res => {
+            if (res) {
+                setGeoLocationList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                    prev[cur.id] = cur
+                    return prev
+                }, {}))
+            }
+        })
+        tagsList_MODEL.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
+            if (res) {
+                setModelList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
+                    prev[cur.id] = cur
+                    return prev
+                }, {}))
+            }
+        })
+    }, [])
+
     // 获取账户列表
     useEffect(() => {
         getAdAccount.run()
@@ -63,10 +110,18 @@ const CreateAd: React.FC = () => {
 
     /** 获取广告详情 */
     useEffect(() => {
-        if (queryForm?.sysAdgroupsId) {
-            getSysAdgroups.run(queryForm?.sysAdgroupsId)
+        if (queryForm?.sysAdgroupId) {
+            getSysAdgroups.run(queryForm?.sysAdgroupId)
+        }
+    }, [queryForm?.sysAdgroupId])
+
+    /** 获取创意详情 */
+    useEffect(() => {
+        if (queryForm?.sysAdcreativeId) {
+            getSysAdcreative.run(queryForm?.sysAdcreativeId)
         }
-    }, [queryForm?.sysAdgroupsId])
+    }, [queryForm?.sysAdcreativeId])
+
     /** 获取定向详情 */
     useEffect(() => {
         if (queryForm?.sysTargetingId) {
@@ -94,12 +149,18 @@ const CreateAd: React.FC = () => {
 
     /** 删除数据源 */
     const sourceDel = (index: number, num: number) => {
-        console.log(index, num);
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
         newArr[index].userActionSetsList?.splice(num, 1)
         setAccountCreateLogs(newArr)
     }
 
+    /** 删除人群包 */
+    const cpDel = (index: number, num: number, key: string) => {
+        let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
+        newArr[index][key]?.splice(num, 1)
+        setAccountCreateLogs(newArr)
+    }
+
     /** 设置落地页 */
     const setPage = (e: any) => {
         setQueryForm({ ...queryForm, sysPageId: e[0]?.id || undefined })
@@ -116,7 +177,7 @@ const CreateAd: React.FC = () => {
             message.error('请选择推广目标')
             return
         }
-        if (!queryForm.sysAdgroupsId) {
+        if (!queryForm.sysAdgroupId) {
             message.error('请先设置广告基本信息')
             return
         }
@@ -124,16 +185,16 @@ const CreateAd: React.FC = () => {
             message.error('请选择定向')
             return
         }
-        // if (!queryForm.sysAdcreativeId) {
-        //     message.error('请设置创意的基本信息')
-        //     return
-        // }
+        if (!queryForm.sysAdcreativeId) {
+            message.error('请设置创意的基本信息')
+            return
+        }
         if (!queryForm.sysPageId) {
             message.error('请选择落地页')
             return
         }
         let data = accountCreateLogs.map((item: any, index) => {
-            return { ...item, id: index + 1, ...queryForm, sysAdGroupData: getSysAdgroups?.data, targetingData: getsysTargeting?.data, pageData: get?.data }
+            return { ...item, ...queryForm, sysAdGroupData: getSysAdgroups?.data, targetingData: getsysTargeting?.data, sysAdcreativeData: { ...getSysAdcreative?.data, adcreativeTemplateAppellation }, pageData: get?.data }
         })
         console.log('data--->', data)
         setTableData(data)
@@ -141,7 +202,67 @@ const CreateAd: React.FC = () => {
 
     const submit = (data: { adName: string, campaignName: string }) => {
         console.log(111111, tableSelect);
-        console.log(222222, data)
+        let params = { ...queryForm, ...data }
+        let accountLogs = tableSelect.map((item: any) => {
+            // userActionSetsList 数据源  productList 商品
+            let data: any = { adAccountId: item.id }
+            if (item?.userActionSetsList?.length > 0) { // 数据源
+                data.userActionSets = item?.userActionSetsList?.map((item: any) => ({ id: item?.id, type: item?.type }))
+            }
+            if (item?.productList?.length > 0) { // 商品
+                data.productId = item?.productList[0].productOuterId
+                data.productCatalogId = item?.productList[0].productCatalogId
+            }
+            if (item?.customAudienceList?.length > 0) {
+                data.customAudience = item?.customAudienceList?.map((item: any) => item.id)
+            }
+            if (item?.excludedCustomAudienceList?.length > 0) {
+                data.excludedCustomAudience = item?.excludedCustomAudienceList?.map((item: any) => item.id)
+            }
+            return data
+        })
+        params.accountCreateLogs = accountLogs
+        createAdBatch.run(params).then(res => {
+            if (res) {
+                sessionStorage.setItem('CAMP', data?.campaignName)
+                message.success('创建成功')
+                // window.location.href = '/#/launchSystemNew/launchManage/taskList'
+            }
+        })
+    }
+
+    /** 清除数据 */
+    const clearData = () => {
+        setTableData([])
+        setTableSelect([])
+    }
+
+    /** 存为预设 */
+    const severBd = () => {
+        // queryForm accountCreateLogs
+        localStorage.setItem('ADQAD', JSON.stringify({
+            queryForm,
+            accountCreateLogs
+        }))
+        message.success('存储成功')
+    }
+
+    /** 清除 */
+    const delBdPlan = () => {
+        localStorage.removeItem('ADQAD')
+        setAccountCreateLogs([])
+        setQueryForm({
+            campaignName: '',  // 计划名称
+            campaignType: 'CAMPAIGN_TYPE_NORMAL', // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
+            promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT', // 推广目标类型
+            speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
+            sysAdgroupId: undefined,  // 广告组内容
+            sysTargetingId: undefined,  // 定向包 id
+            adName: undefined,  // 广告名称
+            configuredStatus: 'AD_STATUS_SUSPEND',  // 广告状态
+            sysAdcreativeId: undefined, // 创意ID
+            sysPageId: undefined, // 落地页Id
+        })
     }
 
     return <Space direction="vertical" style={{ width: '100%' }}>
@@ -158,8 +279,10 @@ const CreateAd: React.FC = () => {
                         filterOption={(input: any, option: any) =>
                             (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
                         }
+                        value={accountCreateLogs?.map((item: { id: number }) => item?.id)}
                         onChange={(e, option) => {
                             setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children, id: item?.value })))
+                            clearData()
                         }}
                     >
                         {getAdAccount?.data?.data?.map((item: any) => <Select.Option value={item.id} key={item.id}>{item.accountId}</Select.Option>)}
@@ -172,7 +295,7 @@ 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 }) }}>
+                    } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e }); clearData() }}>
                         {Object.keys(PromotedObjectType).map(key => {
                             return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
                         })}
@@ -192,16 +315,16 @@ const CreateAd: React.FC = () => {
                                     <Spin spinning={getSysAdgroups.loading}>
                                         <div className={style.centerContent}>
                                             {getSysAdgroups?.data ? <>
-                                                <div>广告名称: {getSysAdgroups?.data?.adgroupName}</div>
-                                                <div>推广目标: {PromotedObjectType[getSysAdgroups?.data?.promotedObjectType]}</div>
-                                                <div>广告版位: {getSysAdgroups?.data?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</div>
-                                                <div>投放日期: {getSysAdgroups?.data?.endDate ? getSysAdgroups?.data?.beginDate + '~' + getSysAdgroups?.data?.endDate : getSysAdgroups?.data?.beginDate + '~' + '长期投放'}</div>
-                                                <div>出价方式: {BidModeEnum[getSysAdgroups?.data?.bidMode]}</div>
-                                                <div>优化目标: {OptimizationGoalEnum[getSysAdgroups?.data?.optimizationGoal]}</div>
-                                                <div>出价类型: {getSysAdgroups?.data?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</div>
-                                                <div>出价策略: {BidStrategyEnum[getSysAdgroups?.data?.bidStrategy]}</div>
-                                                <div>广告出价: {getSysAdgroups?.data?.bidAmount}</div>
-                                                <div>广告日预算: {getSysAdgroups?.data?.dailyBudget || '不限'}</div>
+                                                <div>广告名称: <span>{getSysAdgroups?.data?.adgroupName}</span></div>
+                                                <div>推广目标: <span>{PromotedObjectType[getSysAdgroups?.data?.promotedObjectType]}</span></div>
+                                                <div>广告版位: <span>{getSysAdgroups?.data?.siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span></div>
+                                                <div>投放日期: <span>{getSysAdgroups?.data?.endDate ? getSysAdgroups?.data?.beginDate + '~' + getSysAdgroups?.data?.endDate : getSysAdgroups?.data?.beginDate + '~' + '长期投放'}</span></div>
+                                                <div>出价方式: <span>{BidModeEnum[getSysAdgroups?.data?.bidMode]}</span></div>
+                                                <div>优化目标: <span>{OptimizationGoalEnum[getSysAdgroups?.data?.optimizationGoal]}</span></div>
+                                                <div>出价类型: <span>{getSysAdgroups?.data?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</span></div>
+                                                <div>出价策略: <span>{BidStrategyEnum[getSysAdgroups?.data?.bidStrategy]}</span></div>
+                                                <div>广告出价: <span>{getSysAdgroups?.data?.bidAmount}</span></div>
+                                                <div>广告日预算: <span>{getSysAdgroups?.data?.dailyBudget || '不限'}</span></div>
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
@@ -215,14 +338,58 @@ const CreateAd: React.FC = () => {
                             {/* =============定向包=========== */}
                             <Col className={style.conRightBorder}>
                                 <div className={style.top}>
-                                    定向{/* <span>已选:{1}</span> */}
+                                    定向
+                                    {accountCreateLogs?.length > 0 && queryForm?.sysTargetingId ? <Button type="link" style={{ fontSize: 12, padding: 0 }} onClick={() => setCpVisible(true)}>选择定向包</Button> : <Tooltip title={accountCreateLogs?.length > 0 ? `请先添加定向` : `请先选择媒体账户`}>
+                                        <Button type="link" style={{ fontSize: 12, padding: 0 }}>选择定向包</Button>
+                                    </Tooltip>}
                                 </div>
                                 <div className={style.center}>
                                     <Spin spinning={getsysTargeting.loading}>
                                         <div className={style.centerContent}>
-                                            {getsysTargeting?.data ? <>
-                                                <div>定向名称: {getsysTargeting?.data?.targetingName}</div>
-                                                <div>定向描述: {getsysTargeting?.data?.description}</div>
+                                            {queryForm?.sysTargetingId ? <>
+                                                {getsysTargeting?.data && <Popover
+                                                    content={<div className={style.popover}>
+                                                        <TargetingTooltip data={getsysTargeting?.data} geoLocationList={geoLocationList} modelList={modelList} />
+                                                    </div>}
+                                                    trigger="hover"
+                                                    placement="right"
+                                                >
+                                                    <div className={style.popoverContent}>
+                                                        <div>定向名称: <span>{getsysTargeting?.data?.targetingName}</span></div>
+                                                        <div>定向描述: <span>{getsysTargeting?.data?.description || '<空>'}</span></div>
+                                                    </div>
+                                                </Popover>}
+
+                                                {accountCreateLogs?.map((item: any, index: number) => {
+                                                    if (item?.customAudienceList) {
+                                                        return <div className={style.acc} key={index}>
+                                                            <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                            {item?.customAudienceList?.length > 0 && <>
+                                                                <div className={style.accName}>定向用户群</div>
+                                                                {
+                                                                    item?.customAudienceList?.map((pack: { name: string, id: number }, index1: number) => {
+                                                                        return <div className={style.accCon} key={pack.id}>{pack.name}<CloseOutlined className={style.close} onClick={() => {
+                                                                            cpDel(index, index1, 'customAudienceList')
+                                                                        }} /></div>
+                                                                    })
+                                                                }
+                                                            </>}
+                                                            {item?.excludedCustomAudienceList?.length > 0 && <>
+                                                                <div className={style.accName} style={{ marginTop: 5 }}>排除用户群</div>
+                                                                {
+                                                                    item?.excludedCustomAudienceList?.map((pack: { name: string, id: number }, index1: number) => {
+                                                                        return <div className={style.accCon} key={pack.id}>{pack.name}<CloseOutlined className={style.close} onClick={() => {
+                                                                            cpDel(index, index1, 'excludedCustomAudienceList')
+                                                                        }} /></div>
+                                                                    })
+                                                                }
+                                                            </>}
+                                                        </div>
+                                                    } else {
+                                                        return null
+                                                    }
+                                                })}
+
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
@@ -239,7 +406,7 @@ const CreateAd: React.FC = () => {
                                         {accountCreateLogs?.map((item: any, index: number) => {
                                             if (item?.productList) {
                                                 return <div className={style.acc} key={index}>
-                                                    <div className={style.accName}>{item.adAccountId}</div>
+                                                    <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={() => {
@@ -271,7 +438,7 @@ const CreateAd: React.FC = () => {
                                         {accountCreateLogs?.map((item: any, index: number) => {
                                             if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
                                                 return <div className={style.acc} key={index}>
-                                                    <div className={style.accName}>{item.adAccountId}</div>
+                                                    <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={() => {
@@ -298,13 +465,24 @@ const CreateAd: React.FC = () => {
                     <Col span={8} className={style.conRight}>
                         <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
                         <Row className={style.items}>
+                            {/* 创意 */}
                             <Col span={12} className={style.conRightBorder}>
                                 <div className={style.top}>创意基本信息</div>
                                 <div className={style.center}>
-
+                                    <Spin spinning={getSysAdcreative.loading}>
+                                        <div className={style.centerContent}>
+                                            {(getSysAdcreative?.data && queryForm?.sysAdcreativeId) && <AdcreativeCol data={getSysAdcreative?.data} onChange={(e) => { setAdcreativeTemplateAppellation(e) }} />}
+                                        </div>
+                                    </Spin>
+                                </div>
+                                <div className={style.bottom}>{queryForm?.sysAdgroupId ? <>
+                                    <Button type="link" onClick={() => { setCreativeVisible(true) }}>{queryForm?.sysAdcreativeId ? '修改' : '添加'}</Button>
+                                </> : <Tooltip title="请先设置广告">
+                                    <Button type="link"><span>添加</span></Button>
+                                </Tooltip>}
                                 </div>
-                                <div className={style.bottom}><span onClick={() => { }}>编辑</span></div>
                             </Col>
+                            {/* 落地页 */}
                             <Col span={12} >
                                 <div className={style.top}>
                                     落地页
@@ -323,8 +501,8 @@ const CreateAd: React.FC = () => {
                                                             case 'TOP_IMAGE':
                                                                 return <Image width={80} src={item.topImageSpec.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
                                                             case 'TOP_SLIDER':
-                                                                return <Space>
-                                                                    {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={80} src={url} style={{ borderRadius: 8 }} key={index} />)}
+                                                                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>
@@ -335,10 +513,10 @@ const CreateAd: React.FC = () => {
                                         </div>
                                     </Spin>
                                 </div>
-                                <div className={style.bottom}>{queryForm?.sysAdgroupsId ? <>
+                                <div className={style.bottom}>{queryForm?.sysAdcreativeId ? <>
                                     {queryForm?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
                                     <Button type="link" onClick={() => { setSelectImgVisible(true) }}>{queryForm?.sysPageId ? '修改' : '选择落地页'}</Button>
-                                </> : <Tooltip title="请先设置广告基本信息和创意">
+                                </> : <Tooltip title="请先设置创意">
                                     <Button type="link"><span>选择落地页</span></Button>
                                 </Tooltip>}
                                 </div>
@@ -348,9 +526,9 @@ const CreateAd: React.FC = () => {
                 </Row>
                 {/* =============广告底部按钮=========== */}
                 <Space className={style.bts}>
-                    {/* <Button type='primary' onClick={severBd}>暂存到本地</Button> */}
+                    <Button type='primary' onClick={severBd}>存为预设</Button>
                     <Button type='primary' onClick={preview}><SearchOutlined /> 批量预览广告</Button>
-                    {/* <Button onClick={delBdPlan}>清空本地配置</Button> */}
+                    <Button onClick={delBdPlan}>清空配置/预设</Button>
                 </Space>
             </div>
         </Card>
@@ -360,17 +538,17 @@ const CreateAd: React.FC = () => {
             hoverable
             extra={tableData?.length > 0 ? <Space>
                 <span>推广计划总数:{accountCreateLogs?.length}</span>
-                    <span>广告总数:{accountCreateLogs?.length}</span>
-                    {tableSelect?.length > 0 && <span> 已选:<span style={{ color: '#1890FF' }}>{tableSelect?.length}</span> 条</span>}
-                    {
-                        <Button type='primary' onClick={() => {
-                            if (tableSelect.length === 0) {
-                                message.error('请选择要提交的计划!')
-                                return
-                            };
-                            setSubVisible(true)
-                        }}>批量提交审核</Button>
-                    }
+                <span>广告总数:{accountCreateLogs?.length}</span>
+                {tableSelect?.length > 0 && <span> 已选:<span style={{ color: '#1890FF' }}>{tableSelect?.length}</span> 条</span>}
+                {
+                    <Button type='primary' onClick={() => {
+                        if (tableSelect.length === 0) {
+                            message.error('请选择要提交的计划!')
+                            return
+                        };
+                        setSubVisible(true)
+                    }}>批量提交审核</Button>
+                }
             </Space> : false
             }
         >
@@ -398,21 +576,25 @@ const CreateAd: React.FC = () => {
 
 
         {/* 选择广告 */}
-        {adVisible && <AdModal visible={adVisible} onClose={() => setAdVisible(false)} promotedObjectType={queryForm.promotedObjectType as string} onChange={(e) => { setQueryForm({ ...queryForm, sysAdgroupsId: e }); setAdVisible(false) }} />}
+        {adVisible && <AdModal visible={adVisible} onClose={() => setAdVisible(false)} promotedObjectType={queryForm.promotedObjectType as string} onChange={(e) => { setQueryForm({ ...queryForm, sysAdgroupId: e, sysAdcreativeId: undefined }); setAdVisible(false); clearData() }} sysAdgroupId={queryForm?.sysAdgroupId} />}
         {/* 选择定向 */}
-        {dxVisible && <TargetingModal visible={dxVisible} onClose={() => setDxVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysTargetingId: e }); setDxVisible(false) }} />}
+        {dxVisible && <TargetingModal visible={dxVisible} onClose={() => setDxVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysTargetingId: e }); setDxVisible(false); clearData() }} sysTargetingId={queryForm?.sysTargetingId} />}
+        {/* 选择创意 */}
+        {creativeVisible && <CreativeModal visible={creativeVisible} onClose={() => setCreativeVisible(false)} onChange={(e) => { setQueryForm({ ...queryForm, sysAdcreativeId: e }); setCreativeVisible(false); clearData() }} sysAdcreativeId={queryForm?.sysAdcreativeId} promotedObjectType={queryForm.promotedObjectType as string} />}
         {/* 选择商品 */}
-        {goodsVisible && <GoodsModal visible={goodsVisible} data={accountCreateLogs} onClose={() => setGoodsVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setGoodsVisible(false) }} />}
+        {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) }} />}
+        {sourceVisible && <DataSourceModal visible={sourceVisible} 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) }} />}
+        {idVisible && <IdModal visible={idVisible} data={accountCreateLogs} onClose={() => setIdVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
+        {/* 选择定向包 */}
+        {cpVisible && <CrowdPackModal visible={cpVisible} data={accountCreateLogs} onClose={() => setCpVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setCpVisible(false); clearData() }} />}
         {/* 选择素材 */}
         {selectImgVisible && <SelectCloud visible={selectImgVisible} onClose={() => setSelectImgVisible(false)} onChange={setPage} isBack={false} />}
         {/* 查看落地页 */}
         {lookVisible && <LookLanding visible={lookVisible} onClose={() => setLookVisible(false)} id={queryForm?.sysPageId as any} />}
         {/* 设置名称 */}
-        {subVisible && <SubmitModal visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit}/>}
+        {subVisible && <SubmitModal visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit} ajax={createAdBatch} />}
     </Space>
 }
 

+ 14 - 4
src/pages/launchSystemNew/launchManage/createAd/submitModal.tsx

@@ -1,4 +1,5 @@
-import { Form, Input, message, Modal, Space } from "antd"
+import { AdStatus } from "@/services/launchAdq/enum"
+import { Form, Input, message, Modal, Select, Space } from "antd"
 import React, { useState } from "react"
 
 
@@ -7,15 +8,16 @@ import React, { useState } from "react"
  * 设置名称
  * @returns 
  */
- interface Props {
+interface Props {
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void
+    ajax?: any
 }
 const SubmitModal: React.FC<Props> = (props) => {
 
     /********************/
-    const { visible, onClose, onChange } = props
+    const { visible, onClose, onChange, ajax } = props
     const [form] = Form.useForm()
     /********************/
 
@@ -26,13 +28,14 @@ const SubmitModal: React.FC<Props> = (props) => {
         onChange && onChange(data)
     }
 
-    return <Modal title="设置名称" visible={visible} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
+    return <Modal title="设置名称" visible={visible} confirmLoading={ajax?.loading} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
         <Form
             name="basic"
             form={form}
             labelCol={{ span: 4 }}
             wrapperCol={{ span: 20 }}
             autoComplete="off"
+            initialValues={{ configuredStatus: 'AD_STATUS_SUSPEND' }}
         >
             <Form.Item label="广告名称" name="adName">
                 <Input placeholder="请输入广告名称" />
@@ -40,6 +43,13 @@ const SubmitModal: React.FC<Props> = (props) => {
             <Form.Item label="计划名称" name="campaignName">
                 <Input placeholder="请输入计划名称" />
             </Form.Item>
+            <Form.Item label="广告状态" name="configuredStatus" rules={[{ required: true, message: '请选择广告状态' }]}>
+                <Select placeholder="选择广告状态">
+                    {Object.keys(AdStatus).map(key => {
+                        return <Select.Option value={key} key={key}>{AdStatus[key]}</Select.Option>
+                    })}
+                </Select>
+            </Form.Item>
         </Form>
     </Modal>
 }

+ 127 - 43
src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx

@@ -10,7 +10,7 @@ let columns = () => {
             dataIndex: 'adAccountId',
             key: 'adAccountId',
             align: 'center',
-            width: 100,
+            width: 85,
             fixed: 'left'
         },
         {
@@ -28,7 +28,7 @@ let columns = () => {
             dataIndex: 'promotedObjectType',
             key: 'promotedObjectType',
             align: 'center',
-            width: 120,
+            width: 85,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>{PromotedObjectType[a]}</span>
             }
@@ -48,9 +48,9 @@ let columns = () => {
             dataIndex: 'beginDate',
             key: 'beginDate',
             align: 'center',
-            width: 200,
+            width: 100,
             render: (a: any, b: any) => {
-                return <span>{b?.sysAdGroupData?.endDate ? b?.sysAdGroupData?.beginDate + '~' + b?.sysAdGroupData?.endDate : b?.sysAdGroupData?.beginDate + '~ 长期投放'}</span> 
+                return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.endDate ? b?.sysAdGroupData?.beginDate + '~' + b?.sysAdGroupData?.endDate : b?.sysAdGroupData?.beginDate + '~ 长期投放'}</span> 
             }
         },
         {
@@ -58,7 +58,7 @@ let columns = () => {
             dataIndex: 'date_set1',
             key: 'date_set1',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>全天投放</span>
             }
@@ -68,7 +68,7 @@ let columns = () => {
             dataIndex: 'dailyBudget',
             key: 'dailyBudget',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.dailyBudget || '不限'}</span>
             }
@@ -78,7 +78,7 @@ let columns = () => {
             dataIndex: 'bidMode',
             key: 'bidMode',
             align: 'center',
-            width: 90,
+            width: 80,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>{BidModeEnum[b?.sysAdGroupData?.bidMode]}</span>
             }
@@ -88,7 +88,7 @@ let columns = () => {
             dataIndex: 'bidAmount',
             key: 'bidAmount',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.bidAmount}</span>
             }
@@ -98,25 +98,63 @@ let columns = () => {
             dataIndex: 'optimizationGoal',
             key: 'optimizationGoal',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 return <span style={{ fontSize: "12px" }}>{OptimizationGoalEnum[b?.sysAdGroupData?.optimizationGoal]}</span>
             }
         },
-        // {
-        //     title: '创意名称',
-        //     dataIndex: 'remark',
-        //     key: 'remark',
-        //     align: 'center',
-        //     width: 120,
-        //     render: (a: any, b: any) => {
-        //         return <div className={style.twoText}>
-        //             <Tooltip title={b?.idea?.name}>
-        //                 <span style={{ fontSize: "12px" }}>{b?.idea?.name}</span>
-        //             </Tooltip>
-        //         </div>
-        //     }
-        // },
+        {
+            title: '出价类型',
+            dataIndex: 'smartBidType',
+            key: 'smartBidType',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</span>
+            }
+        },
+        {
+            title: '创意名称',
+            dataIndex: 'adcreativeName',
+            key: 'adcreativeName',
+            align: 'center',
+            width: 120,
+            render: (a: any, b: any) => {
+                return <div className={style.twoText}>
+                    <Tooltip title={b?.sysAdcreativeData?.adcreativeName}>
+                        <span style={{ fontSize: "12px" }}>{b?.sysAdcreativeData?.adcreativeName}</span>
+                    </Tooltip>
+                </div>
+            }
+        },
+        {
+            title: '创意形式',
+            dataIndex: 'adcreativeTemplateAppellation',
+            key: 'adcreativeTemplateAppellation',
+            align: 'center',
+            width: 120,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{b?.sysAdcreativeData?.adcreativeTemplateAppellation}</span>
+            }
+        },
+        {
+            title: '创意文案',
+            dataIndex: 'description',
+            key: 'description',
+            width: 200,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{b?.sysAdcreativeData?.adcreativeElements?.description}</span>
+            }
+        },
+        {
+            title: '文案',
+            dataIndex: 'title',
+            key: 'title',
+            width: 150,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{b?.sysAdcreativeData?.adcreativeElements?.title || '<空>'}</span>
+            }
+        },
         {
             title: '落地页名称',
             dataIndex: 'pageData',
@@ -136,7 +174,7 @@ let columns = () => {
             dataIndex: 'targetingData',
             key: 'targetingData',
             align: 'center',
-            width: 200,
+            width: 120,
             render: (a: any, b: any) => {
                 return <div className={style.twoText}>
                     {
@@ -147,25 +185,71 @@ let columns = () => {
                 </div>
             }
         },
-        // {
-        //     title: '商品',
-        //     dataIndex: 'human_info_goods',
-        //     key: 'human_info_goods',
-        //     align: 'center',
-        //     width: 200,
-        //     render: (a: any, b: any) => {
-        //         if (a) {
-        //             let data = a?.data
-        //             if (data && data?.length > 0) {
-        //                 return <span style={{ fontSize: "12px" }}>{data[0]?.productName + '-' + data[0]?.author}</span>
-        //             }
-        //             return '--'
-        //         } else {
-        //             return '--'
-        //         }
-
-        //     }
-        // }
+        {
+            title: '定向用户群',
+            dataIndex: 'customAudienceList',
+            key: 'customAudienceList',
+            align: 'center',
+            width: 180,
+            render: (a: any, b: any) => {
+                let names = a?.map((item: any) => item.name)
+                return <div className={style.twoText}>
+                    {
+                        names?.length > 0 ? <Tooltip title={names.toString()}>
+                            <span style={{ fontSize: "12px" }}>{names.toString()}</span>
+                        </Tooltip> : '<空>'
+                    }
+                </div>
+            }
+        },
+        {
+            title: '排除用户群',
+            dataIndex: 'excludedCustomAudienceList',
+            key: 'excludedCustomAudienceList',
+            align: 'center',
+            width: 180,
+            render: (a: any, b: any) => {
+                let names = a?.map((item: any) => item.name)
+                return <div className={style.twoText}>
+                    {
+                        names?.length > 0 ? <Tooltip title={names.toString()}>
+                            <span style={{ fontSize: "12px" }}>{names.toString()}</span>
+                        </Tooltip> : '<空>'
+                    }
+                </div>
+            }
+        },
+        {
+            title: '商品',
+            dataIndex: 'productList',
+            key: 'productList',
+            width: 180,
+            align: 'center',
+            render: (a: any, b: any) => {
+                if (a && a?.length > 0) {
+                    let data = a[0]
+                    if (data) {
+                        return <span style={{ fontSize: "12px" }}>{data?.productName + '-' + data?.firstCatalogName + '-' + data?.secondCatalogName}</span>
+                    }
+                    return '<空>'
+                } else {
+                    return '<空>'
+                }
+            }
+        },
+        {
+            title: '数据源',
+            dataIndex: 'userActionSetsList',
+            key: 'userActionSetsList',
+            width: 320,
+            render: (a: any, b: any) => {
+                if (a && a?.length > 0) {
+                    return <span style={{ fontSize: "12px" }}>{a?.map((item: any) => item?.name + '>' + item?.type)?.toString() || '<空>'}</span>
+                } else {
+                    return '<空>'
+                }
+            }
+        }
     ]
 }
 

+ 98 - 0
src/pages/launchSystemNew/launchManage/taskList/index.tsx

@@ -0,0 +1,98 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getListByCorpAccount } from "@/services/enterpriseWeChat/userMange"
+import { PromotedObjectType } from "@/services/launchAdq/enum"
+import { getTaskListApi, TaskListProps } from "@/services/launchAdq/taskList"
+import { Button, Card, Input, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import LookLanding from "../../components/lookLanding"
+import TableData from "../../components/TableData"
+import Log from "./log"
+import tableConfig from './tableConfig'
+
+
+
+const TaskList: React.FC = () => {
+
+    /*************************/
+    const [queryForm, setQueryForm] = useState<TaskListProps>({ pageSize: 20, pageNum: 1 })
+    const [logVisible, setLogVisible] = useState<boolean>(false)
+    const [lookVisible, setLookVisible] = useState<boolean>(false)
+    const [logData, setLogData] = useState<{ taskId: number, campaignName: string } | number>({ taskId: 0, campaignName: '' })
+
+    const getTaskList = useAjax((params) => getTaskListApi(params), { formatResult: true })
+    /*************************/
+
+    useEffect(() => {
+        let campaignName = sessionStorage.getItem('CAMP')
+        if (campaignName) {
+            setQueryForm({ ...queryForm, campaignName })
+            sessionStorage.removeItem('CAMP')
+        }
+    }, [])
+
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+
+    const getList = () => {
+        getTaskList.run(queryForm)
+    }
+
+    const callback = (data: any, type: 'log' | 'page') => {
+        switch (type) {
+            case 'log':
+                setLogData({ ...data })
+                setLogVisible(true)
+                break
+            case 'page':
+                setLogData(data)
+                setLookVisible(true)
+                break
+        }
+    }
+
+    return <>
+        <TableData
+            columns={() => tableConfig(callback)}
+            ajax={getTaskList}
+            dataSource={getTaskList?.data?.data?.records}
+            loading={getTaskList?.loading}
+            scroll={{ y: 600 }}
+            total={getTaskList?.data?.data?.total}
+            page={getTaskList?.data?.data?.current}
+            pageSize={getTaskList?.data?.data?.size}
+            leftChild={<Space>
+                <Input placeholder="计划名称" style={{ width: 150 }} allowClear value={queryForm?.campaignName} onChange={(e) => {setQueryForm({ ...queryForm, campaignName: e.target.value, pageNum: 1 }); sessionStorage.removeItem('CAMP')}} />
+                <Select
+                    placeholder="计划类型"
+                    maxTagCount={1}
+                    allowClear
+                    style={{ width: 150 }}
+                    value={queryForm?.campaignType}
+                    onChange={(e) => { setQueryForm({ ...queryForm, campaignType: e }) }}
+                >
+                    <Select.Option value='CAMPAIGN_TYPE_NORMAL'>普通展示广告</Select.Option>
+                    <Select.Option value='CAMPAIGN_TYPE_WECHAT_MOMENTS'>微信朋友圈广告</Select.Option>
+                </Select>
+                <Select style={{ width: 150 }} allowClear value={queryForm?.promotedObjectType} placeholder="推广目标" showSearch filterOption={(input: any, option: any) =>
+                    (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
+                } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e }) }}>
+                    {Object.keys(PromotedObjectType).map(key => {
+                        return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
+                    })}
+                </Select>
+            </Space>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current, pageSize })
+            }}
+        />
+        {/* 日志 */}
+        {logVisible && <Log {...logData as any} visible={logVisible} onClose={() => setLogVisible(false)} />}
+        {/* 查看落地页 */}
+        {lookVisible && <LookLanding visible={lookVisible} onClose={() => setLookVisible(false)} id={logData as any} />}
+    </>
+}
+
+export default React.memo(TaskList)

+ 81 - 0
src/pages/launchSystemNew/launchManage/taskList/log.tsx

@@ -0,0 +1,81 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getTaskLogListApi, TaskLogListProps } from "@/services/launchAdq/taskList"
+import { Button, Drawer, Select, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import { useModel } from "umi"
+import TableData from "../../components/TableData"
+import tableConfig from './logTableConfig'
+
+interface Props {
+    taskId: number,  // 任务ID
+    campaignName: string, // 计划名称
+    visible?: boolean,
+    onClose?: () => void
+}
+/**
+ * 创建日志
+ * @returns 
+ */
+const Log: React.FC<Props> = (props) => {
+
+    /*****************************/
+    const { taskId, campaignName, visible, onClose } = props
+    const [queryForm, setQueryForm] = useState<TaskLogListProps>({ pageNum: 1, pageSize: 20, taskId })
+
+    const { getAdAccount } = useModel('useLaunchAdq.useAdAuthorize')
+    const getTaskLogList = useAjax((params) => getTaskLogListApi(params), { formatResult: true })
+    /*****************************/
+
+    useEffect(() => {
+        getAdAccount.run()
+    }, [])
+
+    useEffect(() => {
+        getList()
+    }, [queryForm])
+
+    const getList = () => {
+        getTaskLogList.run(queryForm)
+    }
+
+    return <Drawer bodyStyle={{ padding: 0 }} title={campaignName + ' 日志'} width={1000} placement="right" onClose={() => { onClose && onClose() }} visible={visible}>
+        <TableData
+            columns={() => tableConfig()}
+            ajax={getTaskLogList}
+            dataSource={getTaskLogList?.data?.data?.records}
+            loading={getTaskLogList?.loading}
+            scroll={{ y: 600 }}
+            total={getTaskLogList?.data?.data?.total}
+            page={getTaskLogList?.data?.data?.current}
+            pageSize={getTaskLogList?.data?.data?.size}
+            leftChild={<Space>
+                <Select
+                    placeholder="媒体账户"
+                    allowClear
+                    style={{ width: 150 }}
+                    showSearch
+                    value={queryForm?.accountId}
+                    filterOption={(input, option) => {
+                        return (option?.children as unknown as string)?.toString()?.toLowerCase()?.includes(input.toLowerCase())
+                    }}
+                    onChange={(e) => { setQueryForm({ ...queryForm, accountId: e }) }}
+                >
+                    {getAdAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.accountId}</Select.Option>)}
+                </Select>
+                <Select style={{ width: 150 }} allowClear value={queryForm?.createStatus} placeholder="创建状态" showSearch onChange={(e) => { setQueryForm({ ...queryForm, createStatus: e }) }}>
+                    <Select.Option value={-1}>创建失败</Select.Option>
+                    <Select.Option value={0}>创建中</Select.Option>
+                    <Select.Option value={100}>创建成功</Select.Option>
+                </Select>
+            </Space>}
+            onChange={(props: any) => {
+                let { sortData, pagination } = props
+                let { current, pageSize } = pagination
+                setQueryForm({ ...queryForm, pageNum: current, pageSize })
+            }}
+        />
+    </Drawer>
+}
+
+
+export default React.memo(Log)

+ 79 - 0
src/pages/launchSystemNew/launchManage/taskList/logTableConfig.tsx

@@ -0,0 +1,79 @@
+import { Badge, message } from "antd"
+import React from "react"
+function tableConfig(): any {
+    const copy = (str: string) => {
+        let element = document.createElement("textarea");
+        element.id = 'myTextarea'
+        element.textContent = str
+        document.body.append(element);
+        (document.getElementById('myTextarea') as any).select();
+        document.execCommand("Copy")
+        document.body.removeChild(element);
+        message.success(`复制成功:${str}`)
+    }
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 45,
+        },
+        {
+            title: '媒体账户',
+            dataIndex: 'accountId',
+            key: 'accountId',
+            align: 'center',
+            width: 80,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '商品ID',
+            dataIndex: 'productId',
+            key: 'productId',
+            width: 150,
+            ellipsis: true,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '创建状态',
+            dataIndex: 'createStatus',
+            key: 'createStatus',
+            align: 'center',
+            width: 90,
+            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>} />
+                } else {
+                    return <span>--</span>
+                }
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 130,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: <span style={{ padding: '0 8px' }}>失败原因</span>,
+            dataIndex: 'failMsg',
+            key: 'failMsg',
+            align: 'left',
+            ellipsis: true,
+            render: (a: any, b: any) => {
+                return <a style={{ fontSize: "12px" }} onClick={() => copy(a)}>{a || '--'}</a>
+            }
+        },
+    ]
+}
+
+export default tableConfig

+ 115 - 0
src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx

@@ -0,0 +1,115 @@
+import React from "react"
+import { Space } from "antd"
+import { AdStatus, PromotedObjectType, SpeedMode } from "@/services/launchAdq/enum"
+import TargetingPopover from "../../components/targetingPopover"
+function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
+    return [
+        {
+            title: 'ID',
+            dataIndex: 'id',
+            key: 'id',
+            align: 'center',
+            width: 50,
+        },
+        {
+            title: '计划名称',
+            dataIndex: 'campaignName',
+            key: 'campaignName',
+            align: 'left',
+            width: 180,
+            ellipsis: true,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: '计划类型',
+            dataIndex: 'campaignType',
+            key: 'campaignType',
+            align: 'center',
+            width: 120,
+            render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{a === 'CAMPAIGN_TYPE_NORMAL' ? '普通展示广告' : '微信朋友圈广告'}</span>
+                } else {
+                    return <span>--</span>
+                }
+            }
+        },
+        {
+            title: '广告状态',
+            dataIndex: 'configuredStatus',
+            key: 'configuredStatus',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{AdStatus[a]}</span>
+                } else {
+                    return <span>--</span>
+                }
+            }
+        },
+        {
+            title: '推广目标',
+            dataIndex: 'promotedObjectType',
+            key: 'promotedObjectType',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{PromotedObjectType[a]}</span>
+                } else {
+                    return <span>--</span>
+                }
+            }
+        },
+        {
+            title: '投放速度模式',
+            dataIndex: 'speedMode',
+            key: 'speedMode',
+            align: 'center',
+            width: 100,
+            render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{SpeedMode[a]}</span>
+                } else {
+                    return <span>--</span>
+                }
+            }
+        },
+        {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            align: 'center',
+            width: 140,
+            render: (a: any, b: any) => {
+                return <span style={{ fontSize: "12px" }}>{a || '--'}</span>
+            }
+        },
+        {
+            title: <span style={{ marginLeft: 10 }}>操作</span>,
+            dataIndex: 'taskName',
+            key: 'taskName',
+            fixed: 'right',
+            render: (a: any, b: any) => {
+                return <Space style={{ marginLeft: 10 }}>
+                    <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback({ taskId: b.id, campaignName: b.campaignName }, 'log') }}>日志</a>
+                    <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(b.sysPageId, 'page') }}>查看落地页</a>
+                    <TargetingPopover id={b.sysTargetingId}/>
+                    {/* <Popconfirm
+                    title={`你确定删除"${b?.taskName}"这个任务?`}
+                    onConfirm={() => { del(b?.id) }}
+                    okText="Yes"
+                    cancelText="No"
+                >
+                    <a style={{ color: 'red', fontSize: 12 }}>删除</a>
+                </Popconfirm> */}
+                </Space>
+            }
+        }
+    ]
+}
+
+export default tableConfig

+ 1 - 1
src/services/api.ts

@@ -1,4 +1,4 @@
-export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'https://test.api.zanxiangnet.com'
+export let api: any = process.env.NODE_ENV === 'development' ? 'api' : 'http://47.97.38.17/api'
 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`

+ 58 - 48
src/services/launchAdq/createAd.ts

@@ -6,31 +6,37 @@ import { api } from '../api';
  * 获取商品库列表
  */
 export interface CreateAdProps {
-  campaignName: string; // 计划名称
-  campaignType: string; // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH
-  promotedObjectType: string; // 推广目标类型
-  dailyBudget?: number; // 推广计划日预算
-  totalBudget?: number; // 推广计划总预算
-  speedMode: string; // 投放速度模式
-  sysAdgroupsId: number; // 广告组内容
-  sysTargetingId: number; // 定向包 id
-  adName: string; // 广告名称
-  configuredStatus: string; // 广告状态
-  sysAdcreativeId: number; // 创意ID
-  sysPageId: number; // 落地页Id
-  accountCreateLogs: {
-    adAccountId: number; // 媒体账户ID
-    userActionSets?: number; // 数据源
-    conversionId?: number; // 广告组 转化Id
-    productId?: number;
-    enterpriseWx?: any[]; // 企业微信客服组
-  }[];
+    campaignName: string, // 计划名称
+    campaignType: string, // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH 
+    promotedObjectType: string, // 推广目标类型
+    dailyBudget?: number,   // 推广计划日预算
+    totalBudget?: number, // 推广计划总预算
+    speedMode: string, // 投放速度模式
+    sysAdgroupId: number,  // 广告组内容
+    sysTargetingId: number,  // 定向包 id
+    adName: string,  // 广告名称
+    configuredStatus: string,  // 广告状态
+    sysAdcreativeId: number, // 创意ID
+    sysPageId: number, // 落地页Id
+    accountCreateLogs: {
+        adAccountId: number, // 媒体账户ID
+        userActionSets?: {
+            id: number,
+            type: string
+        }[],  // 数据源
+        conversionId?: number, // 广告组 转化Id
+        productCatalogId?: number, // 商品库ID
+        productId?: number, // 商品Id
+        enterpriseWx?: any[]  // 企业微信客服组
+        customAudience?: number[],  // 定向人群
+        excludedCustomAudience?: number[], // 排除人群
+    }[]
 }
-export async function getSysProductCatalogApi(data: CreateAdProps) {
-  return request(api + `/adq/sysProductCatalog/list`, {
-    method: 'POST',
-    data,
-  });
+export async function createAdBatchApi(data: CreateAdProps) {
+    return request(api + `/adq//adCreateTask/createAdBatch`, {
+        method: 'POST',
+        data
+    })
 }
 
 /**
@@ -62,10 +68,10 @@ export async function synGoodsApi(data: number[]) {
  * @returns
  */
 export async function getDataSourceApi(data: number[]) {
-  return request(api + `/adq/userActionSets/allByAccount`, {
-    method: 'POST',
-    data,
-  });
+    return request(api + `/adq/userActionSets/allByAccount`, {
+        method: 'POST',
+        data
+    })
 }
 
 /**
@@ -98,30 +104,34 @@ export async function getIdApi(data: number[]) {
  * @returns
  */
 export async function sysIdApi(data: number[]) {
-  return request(api + `/adq/conversions/syncByAdAccountId`, {
-    method: 'PUT',
-    data,
-  });
+    return request(api + `/adq/conversions/syncByAdAccountId`, {
+        method: 'PUT',
+        data
+    })
 }
+
+
 /**
- * 推广目标
- * @param data
- * @returns
+ * 获取人群包
+ * @param data 
+ * @returns 
  */
-export async function get_promotedObject_allByAccount(data: number[]) {
-  return request(api + `/adq/promotedObject/allByAccount`, {
-    method: 'POST',
-    data,
-  });
+export async function getCrowdPackApi(data: number[]) {
+    return request(api + `/adq/customAudiences/allByAccount`, {
+        method: 'POST',
+        data
+    })
 }
+
+
 /**
- * 同步更新推广目标
- * @param data
- * @returns
+ * 同步人群包
+ * @param data 
+ * @returns 
  */
-export async function put_promotedObject_syncByAdAccountId(data: number[]) {
-  return request(api + `/adq/promotedObject/syncByAdAccountId`, {
-    method: 'PUT',
-    data,
-  });
+ export async function sysCrowdPackApi(data: number[]) {
+    return request(api + `/adq/customAudiences/syncByAdAccountId`, {
+        method: 'PUT',
+        data
+    })
 }

+ 7 - 0
src/services/launchAdq/enum.ts

@@ -351,3 +351,10 @@ export enum LinkPageNameTypeEnum {
   CONTACT_CUSTOMER_SERVICE = '联系客服',
   CONTACT_BUSINESS = '联系商家',
 }
+
+/**人群来源*/
+export enum CrowdSourceEnum {
+  ADVERTISER_OWN_DATA = '一方人群',
+  TENCENT_DATA = '二方人群',
+  UNKNOWN = '未知类型',
+}

+ 40 - 0
src/services/launchAdq/taskList.ts

@@ -0,0 +1,40 @@
+import { request } from 'umi';
+import { api } from '../api'
+
+/**
+ * 获取任务列表
+ * @returns 
+ */
+export interface TaskListProps {
+    pageSize: number,
+    pageNum: number,
+    campaignName?: string, // 计划名称
+    campaignType?: string, // 计划类型
+    promotedObjectType?: string,  // 推广目标类型
+}
+export async function getTaskListApi(data: TaskListProps) {
+    return request(api + '/adq/adCreateTask/list', {
+        method: 'POST',
+        data
+    });
+}
+
+
+
+/**
+ * 任务日志列表
+ * @returns 
+ */
+export interface TaskLogListProps {
+    pageSize: number,
+    pageNum: number,
+    taskId: number,
+    createStatus?: any, // 创建状态  -1:创建失败、0:创建中、100:创建成功
+    accountId?: number, // 账号id
+}
+export async function getTaskLogListApi(data: TaskLogListProps) {
+    return request(api + '/adq/adCreateTask/taskLog', {
+        method: 'POST',
+        data
+    });
+}