wjx пре 2 година
родитељ
комит
9df0ace83d

+ 62 - 0
src/pages/launchSystemNew/components/adPopover/index.tsx

@@ -0,0 +1,62 @@
+import { useAjax } from "@/Hook/useAjax"
+import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
+import { EyeOutlined } from "@ant-design/icons"
+import { Popover, Spin } from "antd"
+import React, { useState } from "react"
+import style from '../targetingPopover/index.less'
+
+interface Props {
+    id: number
+}
+
+/**
+ * 表格查看广告基本信息
+ * @returns 
+ */
+const AdPopover: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { id } = props
+    const [visible, setVisible] = useState<boolean>(false)
+
+    const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
+    /*************************/
+
+
+    const handleVisibleChange = (newVisible: boolean) => {
+        setVisible(newVisible)
+        if (id && newVisible) {
+            getSysAdgroups.run(id)
+        }
+    }
+
+    return <Popover
+        content={<Spin spinning={getSysAdgroups.loading}>
+            <div className={`${style.popover} ${style.aStyle}`}>
+                {getSysAdgroups?.data && <>
+                    <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>
+                </>}
+            </div>
+        </Spin>}
+        // title="定向"
+        trigger="click"
+        placement="left"
+        visible={visible}
+        onVisibleChange={handleVisibleChange}
+    >
+        {/* 查看广告 */}
+        <a style={{ color: '#1890ff', fontSize: 12 }}><EyeOutlined /></a>
+    </Popover>
+}
+
+export default React.memo(AdPopover)

+ 49 - 0
src/pages/launchSystemNew/components/adcreativePopover/index.tsx

@@ -0,0 +1,49 @@
+import { useAjax } from "@/Hook/useAjax"
+import { getSysAdcreativeInfo } from "@/services/launchAdq/creative"
+import { EyeOutlined } from "@ant-design/icons"
+import { Popover, Spin } from "antd"
+import React, { useState } from "react"
+import AdcreativeCol from "../../launchManage/createAd/adcreativeCol"
+import style from '../targetingPopover/index.less'
+
+
+interface Props {
+    id: number
+}
+/**
+ * 表格查看创意基本信息
+ * @returns 
+ */
+const AdcreativePopover: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { id } = props
+    const [visible, setVisible] = useState<boolean>(false)
+
+    const getSysAdcreative = useAjax((params) => getSysAdcreativeInfo(params))
+    /*************************/
+
+    const handleVisibleChange = (newVisible: boolean) => {
+        setVisible(newVisible)
+        if (id && newVisible) {
+            getSysAdcreative.run(id)
+        }
+    }
+
+    return <Popover
+        content={<Spin spinning={getSysAdcreative.loading}>
+            <div className={`${style.popover} ${style.aStyle}`}>
+                {getSysAdcreative?.data && <AdcreativeCol data={getSysAdcreative?.data} />}
+            </div>
+        </Spin>}
+        trigger="click"
+        placement="left"
+        visible={visible}
+        onVisibleChange={handleVisibleChange}
+    >
+        {/* 查看创意 */}
+        <a style={{ color: '#1890ff', fontSize: 12 }}><EyeOutlined /></a>
+    </Popover>
+}
+
+export default React.memo(AdcreativePopover)

+ 1 - 2
src/pages/launchSystemNew/components/creativeModal/index.tsx

@@ -1,8 +1,7 @@
 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 { Col, Input, Modal, Row } from "antd"
 import React, { useCallback, useEffect, useState } from "react"
 import TableData from "../TableData"
 import tableConfig from "./tableConfig"

+ 128 - 0
src/pages/launchSystemNew/components/pageModal/index.tsx

@@ -0,0 +1,128 @@
+import Tables from "@/components/Tables"
+import { useAjax } from "@/Hook/useAjax"
+import { getAdqLandingPageList, putAdqLandingPage } from "@/services/launchAdq/adq"
+import { CheckOutlined, SyncOutlined } from "@ant-design/icons"
+import { Button, Modal, Space } from "antd"
+import React, { useEffect, useState } from "react"
+import style from '../goodsModal/index.less'
+import columns from "./tableConfig"
+
+
+/**
+ * 获取adq落地页
+ * @returns 
+ */
+interface Props {
+    visible?: boolean,
+    onClose?: () => void,
+    onChange?: (data: any) => void,
+    data: any
+}
+const PageModal: React.FC<Props> = (props) => {
+
+    /*************************/
+    const { visible, onClose, data: data1, onChange } = props
+    const [selectAdz, setSelectAdz] = useState<number>(1)   // 选择广告主
+    const [data, setData] = useState<any>(data1)
+    const [queryForm, setQueryForm] = useState<{ accountId?: number, pageSize: number, pageNum: number }>({ pageNum: 1, pageSize: 20 })
+
+    const putAdq = useAjax((params) => putAdqLandingPage(params))
+    const listAjax = useAjax((params) => getAdqLandingPageList(params))
+    /*************************/
+
+    useEffect(() => {
+        if (data?.length > 0) {
+            setQueryForm({ ...queryForm, accountId: data[selectAdz - 1].adAccountId })
+        }
+    }, [selectAdz])
+
+    useEffect(() => {
+        if (queryForm?.accountId) {
+            getList()
+        }
+    }, [queryForm])
+
+    // 获取落地页列表
+    const getList = () => {
+        listAjax.run(queryForm)
+    }
+
+    const handleOk = () => {
+        console.log('data---->', data);
+        
+        onChange && onChange(data)
+    }
+
+    /** 设置选中广告主 */
+    const handleSelectAdz = (value: number, item: any) => {
+        if (value === selectAdz) {
+            return
+        }
+        setSelectAdz(value)
+    }
+
+    /** 表格选折 */
+    const onChangeTable = (selectedRowKeys: React.Key[], selectedRows: any) => {
+        let newData = JSON.parse(JSON.stringify(data))
+        newData[selectAdz - 1]['pageList'] = selectedRows
+        setData([...newData])
+    }
+
+    /** 同步落地页 */
+    const synPageList = () => {
+        let ajaxs = data?.map((item: { id: number }) => putAdq.run(item.id))
+        Promise.all(ajaxs).then(res => {
+            listAjax.refresh()
+        })
+    }
+
+    return <Modal
+        title={<Space>
+            <span>ADQ落地页</span>
+            <Button size="small" onClick={() => { synPageList() }} type="link" loading={putAdq?.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}>
+                <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].pageList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
+                    </div>))}
+            </div>
+            <div className={style.right}>
+                <Space style={{ marginBottom: 10 }} align="end">
+                    <Button icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}></Button>
+                </Space>
+                <Tables
+                    columns={columns()}
+                    dataSource={listAjax?.data?.records?.map((item: any) => ({ ...item, id: item.pageId }))}
+                    size="small"
+                    loading={listAjax?.loading}
+                    scroll={{ y: 300 }}
+                    bordered
+                    defaultPageSize={100}
+                    pageChange={(page: number, pageSize?: number) => {
+                        setQueryForm({ ...queryForm, pageNum: page, pageSize: pageSize as number || 20 })
+                    }}
+                    rowSelection={{
+                        type: 'radio',
+                        selectedRowKeys: data[selectAdz - 1]?.pageList?.map((item: any) => item?.id?.toString()),
+                        onChange: onChangeTable
+                    }}
+                />
+            </div>
+        </div>
+
+    </Modal>
+}
+
+export default React.memo(PageModal)

+ 60 - 0
src/pages/launchSystemNew/components/pageModal/tableConfig.tsx

@@ -0,0 +1,60 @@
+import { PageStatusEnum, PageTypeEnum, SourceTypeEnum } from "@/services/launchAdq/enum"
+import { Badge } from "antd"
+import React from "react"
+let columns = () => [
+    {
+        title: '落地页ID',
+        dataIndex: 'pageId',
+        key: 'pageId',
+        align: 'center',
+        width: 100,
+        render: (a: string) => {
+            return <a>{a}</a>
+        }
+    },
+    {
+        title: '落地页名称',
+        dataIndex: 'pageName',
+        key: 'pageName',
+        align: 'center',
+    },
+    {
+        title: '落地页类型',
+        dataIndex: 'pageType',
+        key: 'pageType',
+        align: 'center',
+        width: 120,
+        render: (a: string) => {
+            return PageTypeEnum[a]
+        }
+    },
+    {
+        title: '落地页状态',
+        dataIndex: 'pageStatus',
+        key: 'pageStatus',
+        align: 'center',
+        width: 90,
+        render: (a: string | number) => {
+            return <Badge status={a === 'NORMAL' ? "processing" : "error"} text={PageStatusEnum[a]} />
+        }
+    },
+    {
+        title: '配置来源',
+        dataIndex: 'sourceType',
+        key: 'sourceType',
+        align: 'center',
+        width: 110,
+        render: (a: any, b: any) => {
+            return SourceTypeEnum[a]
+        }
+    },
+    {
+        title: '创建时间',
+        dataIndex: 'createdTime',
+        key: 'createdTime',
+        align: 'center',
+        width: 160,
+    },
+]
+
+export default columns

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

@@ -5,4 +5,15 @@
     max-height: 150px;
     overflow: hidden;
     overflow-y: auto;
+}
+
+.aStyle {
+    width: 250px;
+    >div {
+        font-size: 12px;
+
+        span {
+            color: #3085ff;
+        }
+    }
 }

+ 1 - 7
src/pages/launchSystemNew/components/targetingTooltip/index.tsx

@@ -58,11 +58,6 @@ const TargetingTooltip: React.FC<Props> = (props) => {
 
     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) => {
@@ -124,9 +119,8 @@ const TargetingTooltip: React.FC<Props> = (props) => {
                         newConten.excludedConvertedAudience = data?.targeting[item]
                         break
                 }
-                setContent(newConten)
             })
-            
+            setContent(newConten)
         }
     }, [data, geoLocationList])
 

+ 33 - 0
src/pages/launchSystemNew/launchManage/createAd/adgroupsCol.tsx

@@ -0,0 +1,33 @@
+import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import React from "react"
+
+
+interface Props {
+    data: any
+}
+/**
+ * 广告基本信息展示
+ * @returns 
+ */
+const AdgroupsCol = React.forwardRef((props: Props, ref) => {
+
+    /*************************/
+    const { data } = props
+    const { adgroupName, promotedObjectType, siteSet, endDate, beginDate, bidMode, optimizationGoal, smartBidType, bidStrategy, bidAmount, dailyBudget } = data
+    /*************************/
+
+    return <>
+        <div>广告名称: <span>{adgroupName}</span></div>
+        <div>推广目标: <span>{PromotedObjectType[promotedObjectType]}</span></div>
+        <div>广告版位: <span>{siteSet?.map((item: string) => SiteSetEnum[item]).toString()}</span></div>
+        <div>投放日期: <span>{endDate ? beginDate + '~' + endDate : beginDate + '~' + '长期投放'}</span></div>
+        <div>出价方式: <span>{BidModeEnum[bidMode]}</span></div>
+        <div>优化目标: <span>{OptimizationGoalEnum[optimizationGoal]}</span></div>
+        <div>出价类型: <span>{smartBidType === 'SMART_BID_TYPE_CUSTOM' ? '手动出价' : '自动出价'}</span></div>
+        <div>出价策略: <span>{BidStrategyEnum[bidStrategy]}</span></div>
+        <div>广告出价: <span>{bidAmount}{`元/${OptimizationGoalEnum[optimizationGoal]}`}</span></div>
+        <div>广告日预算: <span>{dailyBudget || '不限'}</span></div>
+    </>
+})
+
+export default React.memo(AdgroupsCol)

+ 132 - 83
src/pages/launchSystemNew/launchManage/createAd/index.tsx

@@ -2,13 +2,13 @@ import Tables from "@/components/Tables"
 import { useAjax } from "@/Hook/useAjax"
 import { createAdBatchApi, CreateAdProps } from "@/services/launchAdq/createAd"
 import { getSysAdcreativeInfo } from "@/services/launchAdq/creative"
-import { BidModeEnum, BidStrategyEnum, OptimizationGoalEnum, PromotedObjectType, SiteSetEnum } from "@/services/launchAdq/enum"
+import { PromotedObjectType } from "@/services/launchAdq/enum"
 import { getTagsList } from "@/services/launchAdq/global"
 import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
 import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
-import { CloseOutlined, SearchOutlined } from "@ant-design/icons"
+import { CloseOutlined, EditOutlined, QuestionCircleOutlined, SearchOutlined } from "@ant-design/icons"
 import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Popover } from "antd"
-import React, { useEffect, useState } from "react"
+import React, { useEffect, useRef, useState } from "react"
 import { useModel } from "umi"
 import AdModal from "../../components/adModal"
 import CreativeModal from "../../components/creativeModal"
@@ -17,11 +17,13 @@ import DataSourceModal from "../../components/dataSourceModal"
 import GoodsModal from "../../components/goodsModal"
 import IdModal from "../../components/idModal"
 import LookLanding from "../../components/lookLanding"
+import PageModal from "../../components/pageModal"
 import SelectCloud from "../../components/selectCloud"
 import TargetingModal from "../../components/targetingModal"
 import TargetingTooltip from "../../components/targetingTooltip"
 import { WxAutoButton } from "../../req"
 import AdcreativeCol from "./adcreativeCol"
+import AdgroupsCol from "./adgroupsCol"
 import style from './index.less'
 import Selector from "./selector"
 import SubmitModal from "./submitModal"
@@ -38,12 +40,12 @@ const CreateAd: React.FC = () => {
         speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
         sysAdgroupId: undefined,  // 广告组内容
         sysTargetingId: undefined,  // 定向包 id
-        adName: undefined,  // 广告名称
+        adgroupName: 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, customAudienceList?: any, excludedCustomAudienceList?: any }[]>([])  // 账户
+    const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: number, productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any }[]>([])  // 账户
 
     const [adVisible, setAdVisible] = useState<boolean>(false) // 选择广告弹窗控制
     const [dxVisible, setDxVisible] = useState<boolean>(false) // 选择定向弹窗控制
@@ -55,6 +57,7 @@ const CreateAd: React.FC = () => {
     const [lookVisible, setLookVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
     const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
     const [cpVisible, setCpVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
+    const [pageVisible, setPageVisible] = useState<boolean>(false) // 选择云端落地页控制
     const [wxButtonList, setWxButtonList] = useState<WxAutoButton[]>([])
     const [tableData, setTableData] = useState<any[]>([])   // 预览表格
     const [tableSelect, setTableSelect] = useState<any[]>([])
@@ -62,6 +65,7 @@ const CreateAd: React.FC = () => {
     const [modelList, setModelList] = useState<any>({})  // 所有品牌手机
     const [adcreativeTemplateAppellation, setAdcreativeTemplateAppellation] = useState<string>('')  // 创意形式
     const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
+    let refA: { current: { editHandle: () => void } } | any = useRef()
 
 
     const tagsList_REGION = useAjax((params) => getTagsList(params))
@@ -115,6 +119,18 @@ const CreateAd: React.FC = () => {
         }
     }, [queryForm?.sysAdgroupId])
 
+    useEffect(() => {
+        if (getSysAdgroups?.data?.bidMode !== 'BID_MODE_CPM' && accountCreateLogs?.length > 0) {
+            let newAccountCreateLogs = accountCreateLogs?.map((item: any) => {
+                if (item?.customAudienceList) {
+                    delete item?.customAudienceList
+                }
+                return { ...item }
+            })
+            setAccountCreateLogs([...newAccountCreateLogs])
+        }
+    }, [getSysAdgroups?.data?.bidMode])
+
     /** 获取创意详情 */
     useEffect(() => {
         if (queryForm?.sysAdcreativeId) {
@@ -147,6 +163,13 @@ const CreateAd: React.FC = () => {
         setAccountCreateLogs(newArr)
     }
 
+    /** 删除云端内容 */
+    const pageDel = (index: number) => {
+        let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
+        delete newArr[index].pageList
+        setAccountCreateLogs(newArr)
+    }
+
     /** 删除数据源 */
     const sourceDel = (index: number, num: number) => {
         let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
@@ -200,7 +223,7 @@ const CreateAd: React.FC = () => {
         setTableData(data)
     }
 
-    const submit = (data: { adName: string, campaignName: string }) => {
+    const submit = (data: { adgroupName: string, campaignName: string }) => {
         console.log(111111, tableSelect);
         let params = { ...queryForm, ...data }
         let accountLogs = tableSelect.map((item: any) => {
@@ -219,6 +242,9 @@ const CreateAd: React.FC = () => {
             if (item?.excludedCustomAudienceList?.length > 0) {
                 data.excludedCustomAudience = item?.excludedCustomAudienceList?.map((item: any) => item.id)
             }
+            if (item?.pageList) {
+                data.pageId = item?.pageList[0].id
+            }
             return data
         })
         params.accountCreateLogs = accountLogs
@@ -258,7 +284,7 @@ const CreateAd: React.FC = () => {
             speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
             sysAdgroupId: undefined,  // 广告组内容
             sysTargetingId: undefined,  // 定向包 id
-            adName: undefined,  // 广告名称
+            adgroupName: undefined,  // 广告名称
             configuredStatus: 'AD_STATUS_SUSPEND',  // 广告状态
             sysAdcreativeId: undefined, // 创意ID
             sysPageId: undefined, // 落地页Id
@@ -310,22 +336,14 @@ const CreateAd: React.FC = () => {
                         <Row className={style.items}>
                             {/* =============广告基本信息=========== */}
                             <Col className={style.conRightBorder}>
-                                <div className={style.top}>广告基本信息</div>
+                                <div className={style.top}>
+                                    广告基本信息
+                                    {(queryForm?.promotedObjectType && getSysAdgroups?.data) && <a style={{ fontSize: 12 }} onClick={() => refA?.current?.editHandle()}><EditOutlined /></a>}
+                                </div>
                                 <div className={style.center}>
                                     <Spin spinning={getSysAdgroups.loading}>
                                         <div className={style.centerContent}>
-                                            {getSysAdgroups?.data ? <>
-                                                <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} />}
+                                            {getSysAdgroups?.data ? <AdgroupsCol data={getSysAdgroups?.data} /> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
                                 </div>
@@ -339,56 +357,62 @@ const CreateAd: React.FC = () => {
                             <Col className={style.conRightBorder}>
                                 <div className={style.top}>
                                     定向
-                                    {accountCreateLogs?.length > 0 && queryForm?.sysTargetingId ? <Button type="link" style={{ fontSize: 12, padding: 0 }} onClick={() => setCpVisible(true)}>选择定向包</Button> : <Tooltip title={accountCreateLogs?.length > 0 ? `请先添加定向` : `请先选择媒体账户`}>
+                                    {getSysAdgroups?.data?.bidMode === 'BID_MODE_CPM' && <>{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>}
+                                    </Tooltip>}</>}
                                 </div>
                                 <div className={style.center}>
                                     <Spin spinning={getsysTargeting.loading}>
                                         <div className={style.centerContent}>
                                             {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>
-                                                                    })
-                                                                }
-                                                            </>}
+                                                {accountCreateLogs?.some((item: any) => item?.customAudienceList?.length > 0) ? <>
+                                                    {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>
-                                                    } else {
-                                                        return null
-                                                    }
-                                                })}
+                                                    </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
+                                                        }
+                                                    })}
+                                                </> : <>
+                                                    <div>定向名称: <span>{getsysTargeting?.data?.targetingName}</span></div>
+                                                    <div>定向描述: <span>{getsysTargeting?.data?.description || '<空>'}</span></div>
+                                                    <TargetingTooltip data={getsysTargeting?.data} geoLocationList={geoLocationList} modelList={modelList} />
+                                                </>}
+
 
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
@@ -485,30 +509,50 @@ const CreateAd: React.FC = () => {
                             {/* 落地页 */}
                             <Col span={12} >
                                 <div className={style.top}>
-                                    落地页
+                                    <span>落地页 <Tooltip title="云端落地页优先于本地落地页">
+                                        <QuestionCircleOutlined />
+                                    </Tooltip></span>
                                     {wxButtonList?.length > 0 && <Button type="link" size="small">配置客服</Button>}
                                 </div>
                                 <div className={style.center}>
                                     <Spin spinning={get.loading}>
                                         <div className={style.centerContent}>
-                                            {queryForm?.sysPageId ? <>
-                                                <div>落地页名称:{get?.data?.pageName || ''}</div>
-                                                <div>分享名称:{get?.data?.shareContentSpec?.shareTitle || ''}</div>
-                                                <div>分享描述:{get?.data?.shareContentSpec?.shareDescription || ''}</div>
-                                                <div>原生推广页顶部素材预览:
-                                                    <div>{get?.data?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
-                                                        switch (item?.elementType) {
-                                                            case 'TOP_IMAGE':
-                                                                return <Image width={80} src={item.topImageSpec.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
-                                                            case 'TOP_SLIDER':
-                                                                return <Space wrap key={index}>
-                                                                    {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
-                                                                </Space>
-                                                            case 'TOP_VIDEO':
-                                                                return <video src={item.topVideoSpec.videoUrl} width={150} controls key={index}></video>
-                                                        }
-                                                    })}</div>
-                                                </div>
+                                            {queryForm?.sysPageId || accountCreateLogs?.some((item: any) => item.pageList?.length > 0) ? <>
+                                                {(queryForm?.sysPageId && !accountCreateLogs?.every((item: any) => item.pageList?.length > 0)) && <>
+                                                    <div>落地页名称:{get?.data?.pageName || ''}</div>
+                                                    <div>分享名称:{get?.data?.shareContentSpec?.shareTitle || ''}</div>
+                                                    <div>分享描述:{get?.data?.shareContentSpec?.shareDescription || ''}</div>
+                                                    <div style={{ marginBottom: 10 }}>原生推广页顶部素材预览:
+                                                        <div>{get?.data?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
+                                                            switch (item?.elementType) {
+                                                                case 'TOP_IMAGE':
+                                                                    return <Image width={80} src={item.topImageSpec.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
+                                                                case 'TOP_SLIDER':
+                                                                    return <Space wrap key={index}>
+                                                                        {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
+                                                                    </Space>
+                                                                case 'TOP_VIDEO':
+                                                                    return <video src={item.topVideoSpec.videoUrl} width={150} controls key={index}></video>
+                                                            }
+                                                        })}</div>
+                                                    </div>
+                                                </>}
+                                                {accountCreateLogs?.map((item: any, index: number) => {
+                                                    if (item?.pageList && item?.pageList?.length > 0) {
+                                                        return <div className={style.acc} key={index}>
+                                                            <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
+                                                            {
+                                                                item?.pageList?.map((pack: { pageName: string, type: string, id: number }, index1: number) => {
+                                                                    return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.pageName}</span> <CloseOutlined className={style.close} onClick={() => {
+                                                                        pageDel(index)
+                                                                    }} /></div>
+                                                                })
+                                                            }
+                                                        </div>
+                                                    } else {
+                                                        return null
+                                                    }
+                                                })}
                                             </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
                                         </div>
                                     </Spin>
@@ -516,6 +560,9 @@ const CreateAd: React.FC = () => {
                                 <div className={style.bottom}>{queryForm?.sysAdcreativeId ? <>
                                     {queryForm?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
                                     <Button type="link" onClick={() => { setSelectImgVisible(true) }}>{queryForm?.sysPageId ? '修改' : '选择落地页'}</Button>
+                                    {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => { setPageVisible(true) }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
+                                        <Button type="link">云端落地页</Button>
+                                    </Tooltip>}
                                 </> : <Tooltip title="请先设置创意">
                                     <Button type="link"><span>选择落地页</span></Button>
                                 </Tooltip>}
@@ -589,12 +636,14 @@ const CreateAd: React.FC = () => {
         {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() }} />}
+        {/* 选择ADQ落地页 */}
+        {pageVisible && <PageModal visible={pageVisible} data={accountCreateLogs} onClose={() => setPageVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setPageVisible(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} ajax={createAdBatch} />}
+        {subVisible && <SubmitModal data={getSysAdgroups?.data} visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit} ajax={createAdBatch} />}
     </Space>
 }
 

+ 63 - 9
src/pages/launchSystemNew/launchManage/createAd/submitModal.tsx

@@ -1,7 +1,7 @@
 import { AdStatus } from "@/services/launchAdq/enum"
-import { Form, Input, message, Modal, Select, Space } from "antd"
-import React, { useState } from "react"
-
+import { DatePicker, Form, Input, Modal, Radio, Select } from "antd"
+import React, { useEffect, useState } from "react"
+import moment from 'moment';
 
 
 /**
@@ -9,6 +9,7 @@ import React, { useState } from "react"
  * @returns 
  */
 interface Props {
+    data: any,
     visible?: boolean,
     onClose?: () => void,
     onChange?: (data: any) => void
@@ -17,15 +18,50 @@ interface Props {
 const SubmitModal: React.FC<Props> = (props) => {
 
     /********************/
-    const { visible, onClose, onChange, ajax } = props
+    const { visible, onClose, onChange, ajax, data } = props
+    const { endDate, beginDate, bidAmount } = data
     const [form] = Form.useForm()
+    let dateType = Form.useWatch('dateType', form)
+    const [initialValues, setInitialValues] = useState<{ configuredStatus?: string, dateType?: string, date?: [any, any], beginDate?: any, bidAmount?: number, firstDayBeginTime?: any }>({ dateType: '1', configuredStatus: 'AD_STATUS_SUSPEND' })
     /********************/
 
+    useEffect(() => {
+        let params: { configuredStatus?: string, dateType?: string, date?: [any, any], beginDate?: any, bidAmount?: number, firstDayBeginTime?: any } = { bidAmount, configuredStatus: 'AD_STATUS_SUSPEND' }
+        if (endDate) {
+            params.dateType = '1'
+            params.date = [moment(beginDate), moment(endDate)]
+        } else {
+            params.dateType = '2'
+            params.beginDate = moment(beginDate)
+        }
+        setInitialValues(params)
+        setTimeout(() => {
+            form.resetFields();
+        }, 50)
+    }, [])
 
     const handleOk = async () => {
         form.submit()
         let data = await form.validateFields()
-        onChange && onChange(data)
+        const { dateType, date, beginDate, firstDayBeginTime, bidAmount, ...value } = data
+        let params: any = {}
+        if (dateType === '1') { // 选择开始与结束日期
+            if (date) {
+                params.beginDate = moment(date[0]).format('YYYY-MM-DD')
+                params.endDate = moment(date[1]).format('YYYY-MM-DD')
+            }
+        } else { // 长期投放
+            if (beginDate) {
+                params.beginDate = moment(beginDate).format('YYYY-MM-DD')
+            }
+        }
+        if (firstDayBeginTime) {
+            params.firstDayBeginTime = moment(firstDayBeginTime).format('hh:mm:ss')
+        }
+        if (bidAmount) {
+            params.bidAmount = bidAmount
+        }
+        onChange && onChange({ ...value, ...params })
     }
 
     return <Modal title="设置名称" visible={visible} confirmLoading={ajax?.loading} onOk={handleOk} onCancel={() => { onClose && onClose() }}>
@@ -35,21 +71,39 @@ const SubmitModal: React.FC<Props> = (props) => {
             labelCol={{ span: 4 }}
             wrapperCol={{ span: 20 }}
             autoComplete="off"
-            initialValues={{ configuredStatus: 'AD_STATUS_SUSPEND' }}
+            initialValues={{ ...initialValues }}
         >
-            <Form.Item label="广告名称" name="adName">
+            <Form.Item label={<strong>广告名称</strong>} name="adgroupName">
                 <Input placeholder="请输入广告名称" />
             </Form.Item>
-            <Form.Item label="计划名称" name="campaignName">
+            <Form.Item label={<strong>计划名称</strong>} name="campaignName">
                 <Input placeholder="请输入计划名称" />
             </Form.Item>
-            <Form.Item label="广告状态" name="configuredStatus" rules={[{ required: true, message: '请选择广告状态' }]}>
+            <Form.Item label={<strong>广告状态</strong>} 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.Item label={<strong>投放日期</strong>} name='dateType'>
+                <Radio.Group >
+                    <Radio.Button value="1">选择开始与结束日期</Radio.Button>
+                    <Radio.Button value="2">长期投放</Radio.Button>
+                </Radio.Group>
+            </Form.Item>
+            {dateType === '1' && <Form.Item name='date' style={{ marginLeft: 78 }} rules={[{ required: true, message: '请选择日期' }]}>
+                <DatePicker.RangePicker />
+            </Form.Item>}
+            {dateType === '2' && <Form.Item name='beginDate' style={{ marginLeft: 78 }} rules={[{ required: true, message: '请选择日期' }]}>
+                <DatePicker />
+            </Form.Item>}
+            <Form.Item label={<strong>开始时间</strong>} name='firstDayBeginTime'>
+                <DatePicker.TimePicker />
+            </Form.Item>
+            <Form.Item label={<strong>出价</strong>} name='bidAmount' rules={[{ required: true, message: '请输入价格' }]}>
+                <Input placeholder='输入价格 元/千次曝光' />
+            </Form.Item>
         </Form>
     </Modal>
 }

+ 6 - 0
src/pages/launchSystemNew/launchManage/createAd/tableConfig.tsx

@@ -50,6 +50,9 @@ let columns = () => {
             align: 'center',
             width: 100,
             render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{b?.endDate ? b?.beginDate + '~' + b?.endDate : b?.beginDate + '~ 长期投放'}</span> 
+                }
                 return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.endDate ? b?.sysAdGroupData?.beginDate + '~' + b?.sysAdGroupData?.endDate : b?.sysAdGroupData?.beginDate + '~ 长期投放'}</span> 
             }
         },
@@ -90,6 +93,9 @@ let columns = () => {
             align: 'center',
             width: 80,
             render: (a: any, b: any) => {
+                if (a) {
+                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                }
                 return <span style={{ fontSize: "12px" }}>{b?.sysAdGroupData?.bidAmount}</span>
             }
         },

+ 9 - 15
src/pages/launchSystemNew/launchManage/taskList/tableConfig.tsx

@@ -3,6 +3,8 @@ import { Space } from "antd"
 import { AdStatus, PromotedObjectType, SpeedMode } from "@/services/launchAdq/enum"
 import TargetingPopover from "../../components/targetingPopover"
 import { EyeOutlined } from "@ant-design/icons"
+import AdPopover from "../../components/adPopover"
+import AdcreativePopover from "../../components/adcreativePopover"
 function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
     return [
         {
@@ -42,7 +44,7 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             dataIndex: 'configuredStatus',
             key: 'configuredStatus',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 if (a) {
                     return <span style={{ fontSize: "12px" }}>{AdStatus[a]}</span>
@@ -70,7 +72,7 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             dataIndex: 'speedMode',
             key: 'speedMode',
             align: 'center',
-            width: 100,
+            width: 90,
             render: (a: any, b: any) => {
                 if (a) {
                     return <span style={{ fontSize: "12px" }}>{SpeedMode[a]}</span>
@@ -84,10 +86,10 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             dataIndex: 'sysAdgroupId',
             key: 'sysAdgroupId',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 if (a) {
-                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                    return <Space><span style={{ fontSize: "12px" }}>{a}</span><AdPopover id={a}/></Space>
                 } else {
                     return <span>--</span>
                 }
@@ -112,10 +114,10 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             dataIndex: 'sysAdcreativeId',
             key: 'sysAdcreativeId',
             align: 'center',
-            width: 100,
+            width: 80,
             render: (a: any, b: any) => {
                 if (a) {
-                    return <span style={{ fontSize: "12px" }}>{a}</span>
+                    return <Space><span style={{ fontSize: "12px" }}>{a}</span><AdcreativePopover id={a}/></Space>
                 } else {
                     return <span>--</span>
                 }
@@ -129,7 +131,7 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             width: 80,
             render: (a: any, b: any) => {
                 if (a) {
-                    return <Space><span style={{ fontSize: "12px" }}>{a}</span> <a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(b.sysPageId, 'page') }}><EyeOutlined /></a></Space>
+                    return <Space><span style={{ fontSize: "12px" }}>{a}</span><a style={{ color: '#1890ff', fontSize: 12 }} onClick={() => { callback(b.sysPageId, 'page') }}><EyeOutlined /></a></Space>
                 } else {
                     return <span>--</span>
                 }
@@ -153,14 +155,6 @@ function tableConfig(callback: (data: any, type: 'log' | 'page') => void): any {
             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>
-                    {/* <Popconfirm
-                    title={`你确定删除"${b?.taskName}"这个任务?`}
-                    onConfirm={() => { del(b?.id) }}
-                    okText="Yes"
-                    cancelText="No"
-                >
-                    <a style={{ color: 'red', fontSize: 12 }}>删除</a>
-                </Popconfirm> */}
                 </Space>
             }
         }

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

@@ -14,10 +14,14 @@ export interface CreateAdProps {
     speedMode: string, // 投放速度模式
     sysAdgroupId: number,  // 广告组内容
     sysTargetingId: number,  // 定向包 id
-    adName: string,  // 广告名称
+    adgroupName: string,  // 广告名称
     configuredStatus: string,  // 广告状态
     sysAdcreativeId: number, // 创意ID
     sysPageId: number, // 落地页Id
+    beginDate?: string, // 开始日期
+    firstDayBeginTime?: string,  //hh:mm:ss 开始时间
+    endDate?: string, // 结束日期
+    bidAmount?: number, // 出价
     accountCreateLogs: {
         adAccountId: number, // 媒体账户ID
         userActionSets?: {
@@ -30,6 +34,7 @@ export interface CreateAdProps {
         enterpriseWx?: any[]  // 企业微信客服组
         customAudience?: number[],  // 定向人群
         excludedCustomAudience?: number[], // 排除人群
+        pageId?: number,  // 腾讯落地页ID
     }[]
 }
 export async function createAdBatchApi(data: CreateAdProps) {