wjx il y a 10 mois
Parent
commit
4705051ac8

+ 5 - 4
src/pages/launchSystemNew/account/components/openV3All.tsx

@@ -6,10 +6,11 @@ import React from "react"
 
 
 interface Props {
+    adUnitAccount: boolean,
     accountId: any
     onChange?: () => void
 }
-const OpenV3All: React.FC<Props> = ({ accountId, onChange }) => {
+const OpenV3All: React.FC<Props> = ({ accountId, onChange, adUnitAccount }) => {
 
     /*************************************/
     const openAllV3a = useAjax((params) => openAllV3Api(params), { formatResult: true })
@@ -34,10 +35,10 @@ const OpenV3All: React.FC<Props> = ({ accountId, onChange }) => {
     }
 
     return <Space>
-        <Tooltip title="广告业务单元广告主账号全量转3.0">
+        {adUnitAccount && <Tooltip title="广告业务单元广告主账号全量转3.0">
             <Button size="small" style={{ color: "#0eb83a" }} onClick={openAllV3}>{openAllV3a.loading ? <LoadingOutlined /> : <span>全3.0</span>}</Button>
-        </Tooltip>
-        <Tooltip title="广告业务单元广告主账号同步">
+        </Tooltip>}
+        <Tooltip title="广告主账号同步">
             <Button size="small" style={{ color: "#ea5506" }} onClick={asyncAllV3} loading={authSync.loading} icon={<SyncOutlined />}></Button>
         </Tooltip>
     </Space>

+ 1 - 1
src/pages/launchSystemNew/account/game/tableConfig.tsx

@@ -221,7 +221,7 @@ export function columnsMp(
             render: (a: any, b: any) => {
                 return <Space>
                     {(Object.keys(b)?.includes('addV3') && !b.addV3) && <OpenV3 accountId={b?.accountId} onChange={v3Change} />}
-                    {b?.adUnitAccount && <OpenV3All accountId={b?.accountId} onChange={v3Change} />}
+                    <OpenV3All adUnitAccount={b?.adUnitAccount} accountId={b?.accountId} onChange={v3Change} />
                     <Tooltip title="配置服务商">
                         <Button size="small" style={{ color: "#00bcd4" }} onClick={() => setOpenServer([b])} icon={<SettingOutlined />}></Button>
                     </Tooltip>

+ 1 - 1
src/pages/launchSystemNew/account/novel/tableConfig.tsx

@@ -221,7 +221,7 @@ export function columnsMp(
             render: (a: any, b: any) => {
                 return <Space>
                     {(Object.keys(b)?.includes('addV3') && !b.addV3 && !b?.adUnitAccount) && <OpenV3 accountId={b?.accountId} onChange={v3Change} />}
-                    {b?.adUnitAccount && <OpenV3All accountId={b?.accountId} onChange={v3Change} />}
+                    <OpenV3All adUnitAccount={b?.adUnitAccount} accountId={b?.accountId} onChange={v3Change} />
                     <Tooltip title="配置服务商">
                         <Button size="small" style={{ color: "#00bcd4" }} onClick={() => setOpenServer([b])} icon={<SettingOutlined />}></Button>
                     </Tooltip>

+ 19 - 9
src/pages/launchSystemV3/adqv3/ad/index.tsx

@@ -10,7 +10,7 @@ import UpdateAd from "./updateAd";
 import TableData from "@/pages/launchSystemNew/components/TableData";
 import AddDynamic from "../../tencentAdPutIn/create/addDynamic";
 import { arraysHaveSameValues } from "@/utils/utils";
-import { MARKETING_GOAL_ENUM } from "../../tencentAdPutIn/const";
+import { MARKETING_CARRIER_TYPE_ENUM, MARKETING_GOAL_ENUM, MARKETING_TARGET_TYPE_ENUM, SITE_SET_ENUM } from "../../tencentAdPutIn/const";
 const { Text } = Typography;
 
 const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
@@ -21,7 +21,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
     const [selectedRows, setSelectedRows] = useState<any[]>([])
     const [update, setUpdate] = useState<{ visible: boolean }>({ visible: false })
     const [addDynamicVisible, setAddDynamicVisible] = useState<boolean>(false)
-    const [handleType, setHandleType] = useState<number>(2)
+    const [handleType, setHandleType] = useState<number>(1)
 
     const syncBatch = useAjax((params) => syncBatchApi(params))
     const modifyStatusBatch = useAjax((params) => modifyStatusBatchApi(params))
@@ -240,7 +240,7 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                             setSelectedRows([])
                         }}
                         value={handleType}
-                        options={[{ label: '广告操作', value: 1 }, { label: '创意操作', value: 2 }]}
+                        options={[{ label: '广告操作', value: 1 }]}
                     /></Col>
                     {handleType === 1 ? <>
                         <Col><Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={modifyStatusBatch.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus(true)}>启动</Button></Col>
@@ -249,12 +249,22 @@ const Ad: React.FC<ADQV3.AdProps> = ({ userId, creativeHandle }) => {
                     </> : handleType === 2 ? <>
                         <Col><Button type='primary' icon={<PlusOutlined />} disabled={selectedRows.length === 0} onClick={addDynamic}>添加创意</Button></Col>
                         <Col>
-                            {selectedRows?.length > 0 && <Text type="danger" strong style={{ fontSize: 12, marginRight: 6 }}>
-                                {`当前选择:营销目的:${MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal]}`}
-                            </Text>}
-                            <Tooltip title="选择的广告必须与已选广告的营销目的、营销载体、推广内容、广告版位一致">
-                                <QuestionCircleOutlined style={{ color: 'red' }} />
-                            </Tooltip>
+                            <Space>
+                                <Tooltip title="选择的广告必须与已选广告的营销目的、营销载体、推广内容、广告版位一致">
+                                    <QuestionCircleOutlined style={{ color: 'red' }} />
+                                </Tooltip>
+                                {selectedRows?.length > 0 && <div style={{ maxWidth: '380px' }}>
+                                    <Text type="danger" ellipsis={{ tooltip: true }} strong style={{ fontSize: 12 }}>
+                                        {`当前广告选择:
+                                        营销目的:${MARKETING_GOAL_ENUM[selectedRows?.[0]?.marketingGoal]},
+                                        推广产品类型:${MARKETING_TARGET_TYPE_ENUM[selectedRows?.[0]?.marketingTargetType]},
+                                        营销载体类型:${MARKETING_CARRIER_TYPE_ENUM[selectedRows?.[0]?.marketingCarrierType]},
+                                        版位选择:${selectedRows?.[0]?.automaticSiteEnabled ? '自动版位' : '选择特定版位'},
+                                        ${!selectedRows?.[0]?.automaticSiteEnabled && `广告版位:${selectedRows?.[0]?.siteSet.map((item: string | number) => SITE_SET_ENUM[item]).toString()}`}
+                                        `}
+                                    </Text>
+                                </div>}
+                            </Space>
                         </Col>
                     </> : null}
                 </Row>

+ 179 - 11
src/pages/launchSystemV3/tencentAdPutIn/create/addDynamic.tsx

@@ -1,4 +1,4 @@
-import { Card, Drawer, Space, Spin, Typography } from "antd"
+import { Button, Card, Drawer, Empty, Space, Spin, Table, Tabs, Typography, message } from "antd"
 import React, { useEffect, useState } from "react"
 import '../index.less'
 import style from './index.less'
@@ -7,7 +7,11 @@ import Material from "./Material";
 import MaterialText from "./MaterialText";
 import PageList from "./PageList";
 import { DispatchAddelivery } from ".";
-const { Text } = Typography;
+import { CheckOutlined, SearchOutlined } from "@ant-design/icons";
+import WechatAccount from "../../components/WechatAccount";
+import { cartesianProduct } from "@/utils/utils";
+import { columnsAddDynamic } from "./tableConfig";
+const { Text, Title } = Typography;
 
 /**
  * 新增创意
@@ -17,19 +21,131 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
 
     /****************************************/
     const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {} })
+    const { adgroups } = addelivery
     const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
     const [materialData, setMaterialData] = useState<any>({}) // 素材数据
     const [textData, setTextData] = useState<any>({})
     const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([])  // 账户
     const [tableData, setTableData] = useState<any>({})
+    const [activeKey, setActiveKey] = useState<string>()
+    const [dynamicCount, setDynamicCount] = useState<number>(0)
     /****************************************/
 
     useEffect(() => {
-
-    }, [])
+        if (adData?.length > 0) {
+            const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled } = adData[0]
+            setAddelivery({ ...addelivery, adgroups: { marketingGoal, marketingCarrierType, siteSet, automaticSiteEnabled, sceneSpec, marketingAssetOuterSpec: { marketingTargetType } } })
+            let AccountSet = new Set(adData.map(item => item.accountId))
+            setAccountCreateLogs([...AccountSet].map(accountId => ({ accountId })))
+        }
+    }, [adData])
 
     const clearData = () => {
-        setTableData({})
+        setTableData([])
+    }
+
+    const preview = () => {
+        console.log('addelivery------>', addelivery)
+        console.log('accountCreateLogs------>', accountCreateLogs)
+        if (accountCreateLogs?.length === 0) {
+            message.error('请先选择媒体账户')
+            return
+        }
+        const { adgroups, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS } = addelivery
+        if (!(adgroups && Object.keys(adgroups).length)) {
+            message.error('请先配置广告信息')
+            return
+        }
+        if ((['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(adgroups?.marketingAssetOuterSpec?.marketingTargetType) || adgroups?.marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') && !accountCreateLogs?.some(item => item?.wechatChannelList?.length)) {
+            message.error('请先选择公众号')
+            return
+        }
+        if (!(dynamic && Object.keys(dynamic).length)) {
+            message.error('请先配置创意')
+            return
+        }
+        if ((materialData && Object.keys(materialData).length) && !(dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
+            message.error('请先配置创意素材')
+            return
+        }
+        if ((textData && Object.keys(textData).length) && !(dynamicCreativesTextDTOS && Object.keys(dynamicCreativesTextDTOS).length)) {
+            message.error('请先配置创意文案')
+            return
+        }
+        if (!accountCreateLogs?.some(item => item?.pageList?.length)) {
+            message.error('请先选择落地页')
+            return
+        }
+
+        let newTableData = {}, newDynamicCount = 0
+        adData.forEach((ad, index) => {
+            let item = accountCreateLogs.find(a => a.accountId === ad.accountId) as PULLIN.AccountCreateLogsProps
+            let textType = dynamicCreativesTextDTOS.type
+            let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
+            let textDtoLenth = textDto.length
+            let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
+            let data = [{
+                id: ad.adgroupId + '_' + index,
+                pageListDto: item.pageList,                   // 落地页
+                adgroupsDto: ad,
+                dynamicDto: dynamic,                          // 创意信息
+                rowSpan: ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
+            }]
+
+            let newData: any[] = []
+            if ([910].includes(dynamic.creativeTemplateId)) {
+                newData = cartesianProduct(data, item.pageList).map((item, index) => {
+                    let [d1, pageList, num] = item
+                    return {
+                        ...d1,
+                        id: d1.id + '_' + index,
+                        pageListDto: [pageList],
+                        dynamicDto: {
+                            ...d1.dynamicDto,
+                            dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + num
+                        }
+                    }
+                })
+            } else {
+                let newDynamicGroup: any = dynamicMaterialDTos?.dynamicGroup || []
+                if (newDynamicGroup.length > 0 && [0, 1, 2, 3].includes(textType)) {
+                    if (textType === 0) {
+                        newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
+                    } else if (textType === 1) {
+                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
+                    } else if (textType === 2) {
+                        newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
+                    } else if (textType === 3) {
+                        newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
+                            let [dynamicGroup, textDtoData] = item
+                            return {
+                                ...dynamicGroup as any,
+                                textDto: textDtoData
+                            }
+                        })
+                    }
+                }
+                newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
+                    let [d1, group, num] = item
+                    return {
+                        ...d1,
+                        id: d1.id + '_' + index,
+                        dynamicGroup: group,
+                        textDto: (group as any)?.textDto,
+                        dynamicDto: {
+                            ...d1.dynamicDto,
+                            dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + '_' + num
+                        }
+                    }
+                })
+            }
+            newDynamicCount += newData.length
+            newTableData[ad.adgroupId] = newData
+        })
+        setDynamicCount(newDynamicCount)
+        setActiveKey(adData?.[0].adgroupId?.toString())
+        console.log('newTableData-->', newTableData)
+        setTableData(newTableData)
     }
 
     return <Drawer
@@ -41,13 +157,14 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
     >
         <Space direction="vertical" style={{ width: '100%' }}>
             <Spin spinning={false}>
-
-                <Text type="danger" style={{ marginBottom: 5 }}>选择的广告必须与当前已选广告的营销目的、营销载体、推广内容、广告版位一致</Text>
                 <Card
                     size="small"
                     title={<div className={style.cardTitle}>配置区</div>}
                     className={style.createAd}
                 >
+                    <Space wrap>
+                        {(adgroups?.marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT' || adgroups?.marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') && <Button type="primary" danger={!accountCreateLogs?.some(item => item?.wechatChannelList?.length)} onClick={() => { setWechatVisible(true) }}>{accountCreateLogs?.some(item => item?.wechatChannelList?.length) ? <>重新选择公众号 <CheckOutlined style={{ color: '#FFFFFF' }} /></> : '请选择公众号'}</Button>}
+                    </Space>
                     <div className={style.settingsBody}>
                         <div className={style.settingsBody_content}>
                             <DispatchAddelivery.Provider
@@ -69,10 +186,12 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                                         </div>
                                         <div className={style.detail}>
                                             <div className={style.detail_body}>
-
-                                            </div>
-                                            <div className={style.detail_footer}>
-
+                                                <Title level={5} style={{ fontSize: 12 }}>已选广告</Title>
+                                                {adData?.map(item => {
+                                                    return <div key={item.adgroupId}>
+                                                        <div className={style.text}><Text ellipsis={{ tooltip: true }}>{item.adgroupName}</Text></div>
+                                                    </div>
+                                                })}
                                             </div>
                                         </div>
                                     </div>
@@ -90,8 +209,57 @@ const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose
                             </DispatchAddelivery.Provider>
                         </div>
                     </div>
+
+                    <Space className={style.bts} wrap>
+                        <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
+                    </Space>
+
+                    {/* 选择公众号 */}
+                    {wechatVisible && <WechatAccount
+                        visible={wechatVisible}
+                        data={accountCreateLogs}
+                        onClose={() => setWechatVisible(false)}
+                        onChange={(e) => {
+                            setAccountCreateLogs(e);
+                            setWechatVisible(false);
+                            clearData()
+                        }}
+                    />}
                 </Card>
             </Spin>
+
+            <Card
+                className={style.createAd}
+            >
+                {activeKey && tableData && Object.keys(tableData)?.length > 0 ? <div className={style.cardBody}>
+                    <Tabs
+                        onChange={(e) => { setActiveKey(e) }}
+                        type="card"
+                        activeKey={activeKey}
+                        tabBarExtraContent={<Space>
+                            <span>创意总数:{dynamicCount}</span>
+                            <Button type='primary'>提交创建</Button>
+                        </Space>}
+                    >
+                        {adData.map(item => <Tabs.TabPane tab={item.adgroupId} key={item.adgroupId} />)}
+                    </Tabs>
+                    <div className={style.content} style={{ marginTop: 20 }}>
+                        <Table
+                            columns={columnsAddDynamic()}
+                            dataSource={tableData[activeKey]}
+                            size="small"
+                            bordered
+                            scroll={{ x: 1200 }}
+                            rowKey={'id'}
+                            pagination={{
+                                defaultPageSize: 50
+                            }}
+                        />
+                    </div>
+                </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
+                    <Empty description="请先完成模块配置后,再预览广告计划" />
+                </div>}
+            </Card>
         </Space>
     </Drawer>
 }

+ 106 - 0
src/pages/launchSystemV3/tencentAdPutIn/create/tableConfig.tsx

@@ -163,4 +163,110 @@ const columns = (): TableProps<any>['columns'] => {
     ]
 }
 
+
+export const columnsAddDynamic = (): TableProps<any>['columns'] => {
+
+    return [
+        {
+            title: '广告',
+            dataIndex: 'adgroup',
+            key: 'adgroup',
+            align: 'center',
+            children: [
+                {
+                    title: '广告名称',
+                    dataIndex: 'adgroupName',
+                    key: 'adgroupName',
+                    width: 250,
+                    render: (_, b) => {
+                        return <Text style={{ fontSize: 12 }}>{b?.adgroupsDto?.adgroupName}</Text>
+                    },
+                    onCell: (record, index = 0) => ({
+                        rowSpan: !(index % record.rowSpan) ? record.rowSpan : 0
+                    }),
+                },
+            ]
+        },
+        {
+            title: '创意',
+            dataIndex: 'dynamicDto',
+            key: 'dynamicDto',
+            align: 'center',
+            children: [
+                {
+                    title: '创意名称',
+                    dataIndex: 'dynamicCreativeName',
+                    key: 'dynamicCreativeName',
+                    width: 200,
+                    render: (_, b) => {
+                        return <Text style={{ fontSize: 12 }}>{b?.dynamicDto?.dynamicCreativeName}</Text>
+                    }
+                },
+                {
+                    title: '创意素材',
+                    dataIndex: 'dynamicGroup',
+                    key: 'dynamicGroup',
+                    width: 200,
+                    render: (_, b) => {
+                        let deliveryMode = b?.dynamicDto?.deliveryMode
+                        let dynamicGroup = b?.dynamicGroup
+                        if (dynamicGroup && Object.keys(dynamicGroup).length) {
+                            let keys = Object.keys(dynamicGroup)
+                            if (deliveryMode === "DELIVERY_MODE_CUSTOMIZE") {
+                                return <Text style={{ fontSize: 12, color: '#1890ff' }}>已选{(keys.includes('video_id') || keys.includes('short_video1')) ? '1个视频,0张图片' : keys.includes('image_id') ? '0个视频,1张图片' : (keys.includes('image_list') || keys.includes('element_story') ? '1个组图, 0个视频' : '')}</Text>
+                            } else {
+                                return <Text style={{ fontSize: 12 }}>开发中</Text>
+                            }
+                        } else {
+                            return <Text style={{ fontSize: 12 }}>无需配置</Text>
+                        }
+
+                    }
+                },
+                {
+                    title: '创意文案',
+                    dataIndex: 'textDto',
+                    key: 'textDto',
+                    width: 200,
+                    render: (value, b) => {
+                        let deliveryMode = b?.dynamicDto?.deliveryMode
+                        if (value && Object.keys(value).length) {
+                            if (deliveryMode === "DELIVERY_MODE_CUSTOMIZE") {
+                                return <div className={style.detail_body} style={{ height: 'auto' }}>
+                                    {Object.keys(value)?.map((key, index: number) => {
+                                        return <div key={index}>
+                                            {key === 'description' ? <>
+                                                <Title level={5} style={{ fontSize: 12 }}>{'文案'}</Title>
+                                                <div className={style.text}><Text ellipsis={{ tooltip: true }}>{value['description']?.toString()}</Text></div>
+                                            </> : key === 'title' ? <>
+                                                <Title level={5} style={{ fontSize: 12 }}>{'标题'}</Title>
+                                                <div className={style.text}><Text ellipsis={{ tooltip: true }}>{value['title']?.toString()}</Text></div>
+                                            </> : null}
+                                        </div>
+                                    })}
+                                </div>
+                            } else {
+                                return <Text style={{ fontSize: 12 }}>开发中</Text>
+                            }
+                        } else {
+                            return <Text style={{ fontSize: 12 }}>无需配置</Text>
+                        }
+
+                    }
+                },
+                {
+                    title: '跳转类型',
+                    dataIndex: 'pageListDto',
+                    key: 'pageListDto',
+                    width: 200,
+                    render: (_, b) => {
+                        let pageListDto = b?.pageListDto
+                        return <Text style={{ fontSize: 12, wordBreak: 'break-all' }}>原生推广页:{pageListDto?.[0]?.pageName}</Text>
+                    }
+                }
+            ]
+        }
+    ]
+}
+
 export default columns

+ 1 - 1
src/pages/launchSystemV3/tencentAdPutIn/typings.d.ts

@@ -126,7 +126,7 @@ declare namespace PULLIN {
      * 当前广告新增创意
      */
     type NewAddDynamic = {
-        adData: any,
+        adData: any[],
         visible?: boolean,
         onClose?: () => void,
         onChange?: () => void

+ 12 - 0
src/services/adqV3/index.ts

@@ -122,4 +122,16 @@ export async function getDynamicListApi(data: PULLIN.GetDynamicV3LogProps) {
         method: 'POST',
         data
     })
+}
+
+/**
+ * 批量添加创意
+ * @param data 
+ * @returns 
+ */
+export async function createDynamicTaskApi(data: any) {
+    return request(api + `/adq/v3/adgroup/batchCreateCreative`, {
+        method: 'POST',
+        data
+    })
 }