123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041 |
- import { Button, Card, Checkbox, Divider, Empty, Modal, Popconfirm, Select, Space, Spin, Table, Tabs, Tag, Tooltip, message } from "antd"
- import React, { useEffect, useState } from "react"
- import style from './index.less'
- import '../index.less'
- import Selector from "@/pages/launchSystemNew/launchManage/createAd/selector"
- import { CheckOutlined, SearchOutlined } from "@ant-design/icons"
- import Ad from "./Ad"
- import Target from "./Target"
- import { getAccountListApi, getGroupListApi } from "@/services/launchAdq/subgroup"
- import { useAjax } from "@/Hook/useAjax"
- import { useModel } from "umi"
- import GoodsModal from "../../components/GoodsModal"
- import DataSourceModal from "../../components/DataSourceModal"
- import moment from "moment"
- import Dynamic from "./Dynamic"
- import Material from "./Material"
- import MaterialText from "./MaterialText"
- import PageList from "./PageList"
- import { cartesianProduct, distributeArray, processData, randomString, splitArrayIntoRandomChunks } from "@/utils/utils"
- import columns from "./tableConfig"
- import SubmitModal from "./submitModal"
- import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
- import WechatAccount from "../../components/WechatAccount"
- import Title from "antd/lib/typography/Title"
- import { getCreativeDetailsApi } from "@/services/adqV3/global"
- import ConversionSelect from "../../components/ConversionSelect"
- import VideoChannel from "../../components/VideoChannel"
- import Save from "./Save"
- export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
- /**
- * 创建广告
- * @returns
- */
- const Create: React.FC = () => {
- /*******************************************/
- const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
- const { initTargeting } = useModel('useLaunchV3.useTargeting')
- const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
- const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, siteSet, automaticSiteEnabled, sceneSpec, isConversion } = addelivery.adgroups
- const { deliveryMode, creativeTemplateId, creativeComponents } = addelivery.dynamic
- const [accSearch, setAccSearch] = useState<string>()
- const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([]) // 账户
- const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择小说弹窗控制
- const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
- const [channelsProfileVisible, setChannelsProfileVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
- const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
- const [conversionVisible, setConversionVisible] = useState<boolean>(false) // 选择转化归因控制
- const [materialData, setMaterialData] = useState<any>({}) // 素材数据
- const [textData, setTextData] = useState<any>({})
- const [tableData, setTableData] = useState<any>({})
- const [activeKey, setActiveKey] = useState<string>()
- const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
- const [adCount, setAdCount] = useState<number>(0)
- const [dynamicCount, setDynamicCount] = useState<number>(0)
- const [creativeTemplateAppellation, setCreativeTemplateAppellation] = useState<string>()
- const [creativeTemplateStyle, setCreativeTemplateStyle] = useState<string>()
- const getGroupList = useAjax(() => getGroupListApi())
- const createAdgroupTask = useAjax((params) => createAdgroupTaskApi(params))
- const getSelectTaskDetail = useAjax((params) => getSelectTaskDetailApi(params))
- const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
- /*******************************************/
- useEffect(() => {
- // 获取账户组
- getGroupList.run()
- // 获取账户列表
- getAllUserAccount.run()
- initTargeting()
- }, [])
- useEffect(() => {
- if (creativeTemplateId) {
- let params: any = {
- marketingGoal,
- marketingTargetType: marketingAssetOuterSpec.marketingTargetType,
- marketingCarrierType,
- deliveryMode,
- creativeTemplateId,
- wechatSceneSpecPosition: sceneSpec?.wechatPosition,
- dynamicCreativeType: deliveryMode === 'DELIVERY_MODE_COMPONENT' ? 'DYNAMIC_CREATIVE_TYPE_PROGRAM' : 'DYNAMIC_CREATIVE_TYPE_COMMON'
- }
- if (automaticSiteEnabled) {
- params.automaticSiteEnabled = automaticSiteEnabled
- } else {
- params.siteSet = siteSet
- }
- getCreativeDetails.run(params).then(res => {
- if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
- let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
- setCreativeTemplateAppellation(adcreativeTemplateStructAdpermits.creativeTemplateAppellation)
- setCreativeTemplateStyle(adcreativeTemplateStructAdpermits.creativeTemplateStyle)
- let creativeComponents = adcreativeTemplateStructAdpermits?.creativeComponents || []
- let result = processData(creativeComponents);
- let newMaterialData: any = {};
- let newTextData: any = {};
- Object.keys(result).forEach(key => {
- let data = result[key]
- if ((key === 'image_list' || key === 'short_video' || key === 'video' || key === 'image' || key === 'element_story') && data.required) {
- newMaterialData[key] = data
- } else if (key === 'title' || (data.required && key === 'description')) {
- newTextData[key] = data
- }
- })
- setMaterialData(newMaterialData)
- setTextData(newTextData)
- }
- })
- }
- }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled])
- /** 获取分组里账号 */
- const getGroupAccountList = (ids: number[]) => {
- if (ids.length > 0) {
- let data = ids.map(id => getAccountListApi(id))
- Promise.all(data).then(res => {
- if (res?.length > 0 && res.every((item: { code: number }) => item.code === 200)) {
- let userArr: any[] = []
- res.forEach((item: { data: { adAccountList: { accountId: number, id: number }[] } }) => {
- item.data.adAccountList.forEach(acc => {
- let obj = userArr.find((item: { accountId: number }) => item.accountId === acc.accountId)
- if (!obj) {
- userArr.push(acc)
- }
- })
- })
- setAccountCreateLogs(userArr?.map((item) => ({ accountId: item?.accountId })))
- clearData()
- setAddelivery({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
- } else {
- message.error('操作异常')
- }
- })
- } else {
- setAccountCreateLogs([])
- }
- }
- /** 存为预设 */
- const severBd = () => {
- // queryForm accountCreateLogs
- localStorage.setItem('ADQADV3', JSON.stringify({
- addelivery,
- accountCreateLogs,
- materialData,
- textData
- }))
- message.success('存储成功')
- }
- /** 清除 */
- const delBdPlan = () => {
- localStorage.removeItem('ADQADV3')
- setAccountCreateLogs([])
- setMaterialData({})
- setTextData({})
- setAddelivery({
- adgroups: {},
- targeting: [],
- dynamic: {},
- dynamicMaterialDTos: {},
- dynamicCreativesTextDTOS: {},
- mediaType: 0
- })
- setTableData({})
- }
- /**数据回填 */
- useEffect(() => {
- let taskId = sessionStorage.getItem('TASKID3.0')
- let adqAdData = localStorage.getItem('ADQADV3')
- if (taskId) {
- getSelectTaskDetail.run(taskId).then(res => {
- if (res) {
- const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO: { mediaType, ...dynamic }, dynamicCreativesTextDTO, dynamicMaterialDTOS } = res
- let beginDate = adgroupDTO.beginDate
- let endDate = adgroupDTO.endDate
- if (beginDate && moment(beginDate) < moment()) {
- beginDate = moment().format('YYYY-MM-DD')
- endDate = moment().add(7, 'day').format('YYYY-MM-DD')
- message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
- }
- let dynamicGroup: any[] = []
- if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
- dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
- let { type, valueJson } = item[0]
- let value = JSON.parse(valueJson).value
- if (type === 'image') {
- return { image_id: { id: value.imageId, url: value.imageUrl, materialType: value.materialType } }
- } else if (type === 'image_list' || type === 'element_story') {
- return { [type]: value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType })) }
- } else if (type === 'short_video' || type === 'video') {
- let field = type === 'video' ? 'video_id' : 'short_video1'
- let videoData: any = {}
- videoData[field] = { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
- if (value.imageUrl) {
- videoData['cover_id'] = { materialType: value.materialCoverType, url: value.imageUrl, id: value.iamgeId }
- }
- return videoData
- } else {
- return {}
- }
- })
- } else { // 组件化创意
- dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
- return {
- list: item?.map((i: any) => {
- let { type, valueJson } = i
- let value = JSON.parse(valueJson).value
- if (type === 'image') {
- return { id: value.imageId, url: value.imageUrl, materialType: value.materialType }
- } else if (type === 'image_list') {
- return value.list.map((l: { imageUrl: any; imageId: any; materialType: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType }))
- } else if (type === 'video') {
- return { materialType: value.materialType, url: value.videoUrl, id: value.videoId }
- } else {
- return {}
- }
- })
- }
- })
- }
- let isConversion = false
- setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
- const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO, wechatAppletList } = accountIdParamVOMap[accountId]
- let data: PULLIN.AccountCreateLogsProps = {
- accountId: Number(accountId),
- productList: productDTOS
- }
- if (wechatOfficialAccountsVO) {
- data.wechatChannelList = [wechatOfficialAccountsVO]
- }
- if (pageList || landingPageVOS || wechatAppletList) {
- data.pageList = pageList || landingPageVOS || wechatAppletList?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))
- }
- if (userActionSetsList) {
- data.userActionSetsList = userActionSetsList
- }
- if (conversionInfo) {
- isConversion = true
- data.newConversionList = [conversionInfo]
- }
- if (wechatChannelVO) {
- data.videoChannelList = [wechatChannelVO]
- }
- return data
- }))
- setAddelivery({
- adgroups: { ...adgroupDTO, isConversion, adgroupName: adgroupDTO.adgroupName + '_副本' + randomString(true, 3, 5), endDate, beginDate },
- targeting: targetings.map((item: any) => {
- const { targetingName, ...targeting } = item
- return { targetingName, targeting }
- }),
- dynamic,
- dynamicMaterialDTos: dynamicGroup.length > 0 ? { dynamicGroup } : {},
- dynamicCreativesTextDTOS: dynamicCreativesTextDTO,
- mediaType: mediaType || 0
- })
- sessionStorage.removeItem('TASKID3.0')
- }
- })
- } else if (adqAdData) {
- const { addelivery, accountCreateLogs, materialData, textData } = JSON.parse(adqAdData)
- if (addelivery?.adgroups) {
- if (addelivery?.adgroups?.beginDate && moment(addelivery?.adgroups?.beginDate) < moment()) {
- addelivery.adgroups.beginDate = moment().format('YYYY-MM-DD')
- message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
- }
- if (addelivery?.adgroups?.endDate && moment(addelivery?.adgroups?.endDate) < moment()) {
- addelivery.adgroups.endDate = moment().format('YYYY-MM-DD')
- message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
- }
- }
- setAddelivery({ ...addelivery })
- setAccountCreateLogs(accountCreateLogs)
- setMaterialData(materialData)
- setTextData(textData)
- }
- }, [])
- // 预览
- const preview = () => {
- if (accountCreateLogs?.length === 0) {
- message.error('请先选择媒体账户')
- return
- }
- const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
- if (!(adgroups && Object.keys(adgroups).length)) {
- message.error('请先配置广告信息')
- return
- }
- if (!(targeting?.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 (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType) && !accountCreateLogs?.some(item => item?.productList?.length)) {
- message.error(marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_FICTION' ? '请先选择小说' : '请先选择短剧')
- return
- }
- if ((['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType) || marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT' || dynamic?.creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL') && !accountCreateLogs?.some(item => item?.wechatChannelList?.length)) {
- message.error('请先选择公众号')
- return
- }
- if (creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE' && !accountCreateLogs?.some(item => item?.videoChannelList?.length)) {
- message.error('请先选择视频号')
- return
- }
- if (isConversion && !accountCreateLogs?.some(item => item?.newConversionList?.length)) {
- message.error('请先选择转化归因')
- return
- }
-
- if (!accountCreateLogs?.some(item => item?.pageList?.length)) {
- message.error('请先选择落地页')
- return
- }
- let newTableData: any = {}
- let newAdCount = 0, newdynamicCount = 0
- let textType = dynamicCreativesTextDTOS.type
- let textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
- let textDtoLenth = textDto.length
- let dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
- let newDynamicGroup: any = []
- if (![910].includes(dynamic.creativeTemplateId)) {
- newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
- if (newDynamicGroup.length > 0 && [0, 1, 2, 3, 4].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 && mediaType === 0) || (textType === 4 && mediaType === 1)) {
- newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
- let [dynamicGroup, textDtoData] = item
- return {
- ...dynamicGroup as any,
- textDto: textDtoData
- }
- })
- }
- }
- }
- // 创意组平均分配到广告逻辑
- let averageAdDynamicList: any[] = []
- if ((mediaType === 1 || mediaType === 2) && newDynamicGroup.length) {
- let adLength = 0
- accountCreateLogs.forEach(item => {
- let productList: any[] = []
- if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说 短剧
- productList = item?.productList || []
- } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
- productList = item?.wechatChannelList || []
- } else {
- productList = [{}]
- }
- adLength += productList.length * targeting.length
- })
- if (mediaType === 1) {
- if (textType === 4) {
- if (adLength > newDynamicGroup.length) {
- message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
- return
- }
- averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
- } else {
- if (adLength > dynamicGroupLength) {
- message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
- return
- }
- averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
- }
- } else if (mediaType === 2) {
- if (adLength < dynamicGroupLength) {
- message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
- return
- }
- }
- }
- if (textType === 1) {
- if (dynamicGroupLength !== textDtoLenth) {
- message.error(`当前创意文案是“创意组一一对应”模式,创意组总数(${dynamicGroupLength})要等于创意文案总数(${textDtoLenth})`)
- return
- }
- if (!dynamicCreativesTextDTOS.dynamicCreativesTextDetailDTOList.every((item: {}) => item && Object.keys(item).length)) {
- message.error('创意文案配置错误,内容空')
- return
- }
- }
- // 落地页平均分配判断数量
- if ([910].includes(dynamic.creativeTemplateId) && dynamic?.landingPageType === 1) {
- let targetingLength = targeting.length
- if (accountCreateLogs.some(item => {
- let productListLength = item?.productList?.length || 1
- let total = targetingLength * productListLength
- let pageLength = item.pageList.length
- if (total > pageLength) {
- message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
- return true
- }
- return false
- })) {
- return
- }
- }
- let accountIndex = 0, accountIndex1 = 0
- accountCreateLogs.forEach(item => {
- let productList: any[] = [{}]
- if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
- productList = item?.productList || []
- } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
- productList = item?.wechatChannelList || []
- }
- let data = cartesianProduct(productList, targeting).map(newD => {
- let [productDto, targetDto, index] = newD
- let suffix = '_' + item.accountId + '_' + index
- let averageAdDynamic = averageAdDynamicList?.[accountIndex]
- let dat: any = {
- id: item.accountId + '_' + index,
- accountId: item.accountId, // 账户
- userActionSetsList: item.userActionSetsList, // 数据源
- conversionList: item.newConversionList, // 转化归因
- pageListDto: item.pageList, // 落地页
- productDto, // 商品
- targetDto: { // 定向
- ...targetDto,
- targetingName: targetDto.targetingName + suffix
- },
- adgroupsDto: { // 广告信息
- ...adgroups,
- adgroupName: adgroups.adgroupName + suffix
- },
- dynamicDto: dynamic, // 创意信息
- averageAdDynamic,
- rowSpan: (mediaType === 1 && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
- }
- if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
- dat.marketingCarrierDto = item?.wechatChannelList
- }
- accountIndex += 1
- return dat
- })
- newAdCount += data.length
- let newData: any[] = []
- if ([910].includes(dynamic.creativeTemplateId)) {
- if (dynamic?.landingPageType === 1) {
- let averageAdPageList: any[] = distributeArray(item.pageList, productList.length * targeting.length)
- data.forEach((item, aIndex) => {
- let aPageList: any[] = averageAdPageList[aIndex]
- aPageList.forEach((page, index) => {
- newData.push({
- ...item,
- id: item.id + '_' + index,
- pageListDto: [page],
- dynamicDto: {
- ...item.dynamicDto,
- dynamicCreativeName: item.dynamicDto.dynamicCreativeName + index
- },
- rowSpan: index === 0 ? aPageList.length : 0,
- adLength: data.length,
- isRowSpan: true
- })
- })
- })
- } else {
- 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 {
- if (mediaType === 1) {
- data.forEach(item => {
- const { averageAdDynamic, ...ad } = item
- if (textType === 3) {
- let rowSpan = textDtoLenth * averageAdDynamic.length
- cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
- let [textValue, aad] = taad
- newData.push({
- ...ad,
- id: ad.id + '_' + index,
- dynamicGroup: aad,
- textDto: textValue,
- rowSpan: index === 0 ? rowSpan : 0,
- adLength: data.length,
- isRowSpan: true
- })
- })
- } else if (textType === 4) {
- averageAdDynamic.forEach((aad: any, index: number) => {
- newData.push({
- ...ad,
- id: ad.id + '_' + index,
- dynamicGroup: aad,
- textDto: aad?.textDto,
- adLength: data.length,
- rowSpan: index === 0 ? averageAdDynamic.length : 0,
- isRowSpan: true
- })
- })
- } else {
- averageAdDynamic.forEach((aad: any, index: number) => {
- newData.push({
- ...ad,
- id: ad.id + '_' + index,
- dynamicGroup: aad,
- textDto: aad?.textDto,
- adLength: data.length
- })
- })
- }
- })
- } else if (mediaType === 2) {
- data.forEach((item) => {
- const { averageAdDynamic, ...ad } = item
- if (textType === 3) {
- cartesianProduct(textDto, [newDynamicGroup[accountIndex1 % newDynamicGroup.length]]).forEach((taad: any, i) => {
- let [textValue, aad, index] = taad
- newData.push({
- ...ad,
- id: ad.id + '_' + index,
- dynamicGroup: aad,
- textDto: textValue,
- rowSpan: i === 0 ? textDto.length : 0,
- adLength: data.length,
- isRowSpan: true
- })
- })
- } else {
- let { textDto, ...dynamicGroup } = newDynamicGroup[accountIndex1 % newDynamicGroup.length]
- newData.push({
- ...ad,
- dynamicGroup,
- textDto,
- rowSpan: 1,
- adLength: data.length
- })
- }
- accountIndex1 += 1
- })
- } else { // 全账号复用创意组 所有广告一样的创意组
- newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
- let [d1, group] = item
- return {
- ...d1,
- id: d1.id + '_' + index,
- dynamicGroup: group,
- textDto: (group as any)?.textDto,
- adLength: data.length
- }
- })
- }
- }
- newdynamicCount = newdynamicCount + newData.length
- newTableData[item.accountId] = newData
- })
- setAdCount(newAdCount)
- setDynamicCount(newdynamicCount)
- setActiveKey(accountCreateLogs?.[0].accountId?.toString())
- console.log('newTableData-->', newTableData)
- setTableData(newTableData)
- }
- // 提交
- const onSubmit = (values: any) => {
- const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = JSON.parse(JSON.stringify(addelivery)) as PULLIN.AddeliveryProps
- let dynamicMaterialDTOS = []
- if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
- if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
- let mType = Object.keys(materialData)[0];
- dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: any) => {
- if (mType === 'image') {
- return [{
- type: mType,
- valueJson: JSON.stringify({
- value: {
- imageUrl: item?.image_id?.url,
- imageId: item?.image_id?.id,
- materialType: item?.image_id?.materialType
- }
- })
- }]
- } else if (mType === 'image_list' || mType === 'element_story') {
- let key = 'image_list'
- if (mType === 'element_story') {
- key = 'element_story'
- }
- let list = item?.[key]?.map((l: any) => {
- return {
- imageUrl: l?.url,
- imageId: l?.id,
- materialType: l?.materialType
- }
- })
- return [{
- type: mType,
- valueJson: JSON.stringify({
- value: {
- list
- }
- })
- }]
- } else if (['short_video', 'video'].includes(mType)) {
- let value: any = {
- materialType: item?.video_id?.materialType || item?.short_video1?.materialType || 0,
- videoUrl: item?.video_id?.url || item?.short_video1?.url,
- videoId: item?.video_id?.id || item?.short_video1?.id
- }
- if (item?.cover_id?.url) {
- value.imageUrl = item?.cover_id?.url
- value.imageId = item?.cover_id?.id
- value.materialCoverType = item?.cover_id?.materialType
- }
- return [{
- type: mType,
- valueJson: JSON.stringify({
- value
- })
- }]
- }
- return [{
- type: mType,
- valueJson: ''
- }]
- })
- }
- } else {
- if (dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length) {
- dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: { list: any[] }) => {
- return item.list.map(l => {
- if (Array.isArray(l)) {
- return {
- type: 'image_list',
- valueJson: JSON.stringify({
- value: {
- list: l?.map((i: any) => {
- return {
- imageUrl: i?.url,
- imageId: i?.id,
- materialType: i?.materialType
- }
- })
- }
- })
- }
- } else if (l?.url?.includes('mp4')) {
- return {
- type: 'video',
- valueJson: JSON.stringify({
- value: {
- materialType: l?.materialType,
- videoUrl: l?.url,
- videoId: l?.id
- }
- })
- }
- } else {
- return {
- type: 'image',
- valueJson: JSON.stringify({
- value: {
- imageUrl: l?.url,
- imageId: l?.id,
- materialType: l?.materialType
- }
- })
- }
- }
- })
- })
- }
- }
- let accountIdParamDTOMap: any = {}
- accountCreateLogs.forEach(item => {
- let { pageList, productList, userActionSetsList, accountId, wechatChannelList, newConversionList, videoChannelList } = item
- let userActionSetsListDto = userActionSetsList?.map((item: any) => ({ id: item?.userActionSetId, type: item?.type })) // dataSourceId
- let map: any = {
- userActionSetsList: userActionSetsListDto,
- pageList: pageList?.map((item: { pageId: any }) => item.pageId)
- }
- if (productList && ['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) {
- map.productDTOS = productList?.map(item => {
- return { productId: item.marketingAssetId }
- })
- }
- if (wechatChannelList && (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType) || marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT' || dynamic?.creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL')) {
- map.wechatChannelId = wechatChannelList?.[0]?.wechatOfficialAccountId
- }
- if (newConversionList && isConversion) {
- map.conversionId = newConversionList?.[0]?.conversionId
- }
- if (videoChannelList && creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE') {
- map.videoChannelId = videoChannelList?.[0]?.wechatChannelsAccountId
- }
- accountIdParamDTOMap[accountId] = map
- })
- let dynamicCreativesDTO = { ...dynamic, mediaType }
- if (dynamic.deliveryMode === 'DELIVERY_MODE_COMPONENT') {
- dynamicCreativesDTO.creativeTemplateId = 711
- }
- delete adgroups?.isConversion // 前端控制新链路字段
- let params = {
- ...values,
- adgroupDTO: adgroups,
- targetings: targeting.map(item => ({ targetingName: item.targetingName, ...item?.targeting || {} })),
- dynamicCreativesDTO,
- dynamicCreativesTextDTOS,
- dynamicMaterialDTOS,
- accountIdParamDTOMap
- }
- // setSubVisible(false)
- createAdgroupTask.run(params).then(res => {
- if (res) {
- Modal.success({
- content: '任务提交成功',
- bodyStyle: { fontWeight: 700 },
- okText: '跳转任务列表',
- closable: true,
- onOk: () => {
- sessionStorage.setItem('CAMPV3', values?.taskName)
- window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
- },
- onCancel: () => {
- setSubVisible(false)
- }
- })
- }
- })
- }
- const clearData = () => {
- setTableData({})
- }
- return <Space direction="vertical" style={{ width: '100%' }}>
- <Spin spinning={createAdgroupTask.loading || getSelectTaskDetail.loading || getCreativeDetails.loading}>
- <Card
- size="small"
- title={
- <div>
- <Space>
- <div className={style.cardTitle}>配置区</div>
- </Space>
- </div>
- }
- className={style.createAd}
- >
- <Space wrap>
- <Selector label="媒体账户组">
- <Select
- mode="multiple"
- style={{ minWidth: 200 }}
- placeholder="快捷选择媒体账户组"
- maxTagCount={1}
- allowClear
- bordered={false}
- filterOption={(input: any, option: any) => {
- return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
- }}
- onChange={(e, option) => { getGroupAccountList(e) }}
- >
- {getGroupList?.data && getGroupList?.data?.map((item: any) => <Select.Option value={item.groupId} key={item.groupId}>{item.groupName}</Select.Option>)}
- </Select>
- </Selector>
- <Selector label="媒体账户">
- <Select
- mode="multiple"
- style={{ minWidth: 200, maxWidth: 500 }}
- placeholder="媒体账户(多个,,空格换行)"
- maxTagCount={1}
- allowClear
- bordered={false}
- maxTagPlaceholder={
- <Tooltip
- color="#FFF"
- title={<span style={{ color: '#000' }}>
- {accountCreateLogs?.filter((item, index) => index !== 0)
- ?.map((item, index) => <Tag
- key={index}
- closable
- onClose={() => {
- setAccountCreateLogs(accountCreateLogs?.filter(item1 => item1.accountId !== item.accountId))
- // setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
- // clearData()
- }}
- >{item.accountId}</Tag>)}</span>
- }
- >
- <span>+{accountCreateLogs?.length > 1 ? accountCreateLogs.length - 1 : 0}</span>
- </Tooltip>
- }
- autoClearSearchValue={false}
- filterOption={(input: any, option: any) => {
- let newInput: string[] = input ? input?.split(/[,,\n\s]+/ig).filter((item: any) => item) : []
- return newInput?.some(val => option!.children?.toString().toLowerCase()?.includes(val))
- }}
- value={accountCreateLogs?.map(item => item?.accountId)}
- onChange={(e) => {
- setAccountCreateLogs(e?.map((item: any) => ({ accountId: item })))
- }}
- searchValue={accSearch}
- onSearch={(val) => {
- setAccSearch(val)
- }}
- dropdownRender={menu => (
- <>
- {menu}
- <Divider style={{ margin: '8px 0' }} />
- <Space style={{ padding: '0 8px 4px' }}>
- <Checkbox onChange={(e) => {
- let data = []
- if (e.target.checked) {
- data = JSON.parse(JSON.stringify(getAllUserAccount?.data?.data))
- if (accSearch) {
- let newAccSearch = accSearch?.split(/[,,\n\s]+/ig).filter((item: any) => item)
- data = data?.filter((item: any) => newAccSearch?.some(val => item!.accountId?.toString().toLowerCase()?.includes(val)))
- }
- }
- // setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
- setAccountCreateLogs(data?.map((item: any) => ({ accountId: item?.accountId })))
- // clearData()
- }}>全选</Checkbox>
- </Space>
- </>
- )}
- >
- {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
- </Select>
- </Selector>
- {accountCreateLogs?.length > 0 && <>
- {['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType) && <Button type="primary" danger={!accountCreateLogs?.some(item => item?.productList?.length)} onClick={() => { setGoodsVisible(true) }}>{accountCreateLogs?.some(item => item?.productList?.length) ? <>重新选择{marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_FICTION' ? '小说' : '短剧'} <CheckOutlined style={{ color: '#FFFFFF' }} /></> : `请选择${marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_FICTION' ? '小说' : '短剧'}`}</Button>}
- {(marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT' || marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT' || creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL') && <Button type="primary" danger={!accountCreateLogs?.some(item => item?.wechatChannelList?.length)} onClick={() => { setWechatVisible(true) }}>{accountCreateLogs?.some(item => item?.wechatChannelList?.length) ? <>重新选择公众号 <CheckOutlined style={{ color: '#FFFFFF' }} /></> : '请选择公众号'}</Button>}
- {creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE' && <Button type="primary" danger={!accountCreateLogs?.some(item => item?.videoChannelList?.length)} onClick={() => { setChannelsProfileVisible(true) }}>{accountCreateLogs?.some(item => item?.videoChannelList?.length) ? <>重新选择视频号 <CheckOutlined style={{ color: '#FFFFFF' }} /></> : '请选择视频号'}</Button>}
- {!isConversion ?
- <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
- :
- <Button type="primary" danger={!accountCreateLogs?.some(item => item?.newConversionList?.length)} onClick={() => { setConversionVisible(true) }}>{accountCreateLogs?.some(item => item?.newConversionList?.length) ? <>重新选择转化归因<CheckOutlined style={{ color: '#FFF' }} /></> : '请选择转化归因'}</Button>
- }
- </>}
- </Space>
- <div className={style.settingsBody}>
- <div className={style.settingsBody_content}>
- <DispatchAddelivery.Provider
- value={{
- addelivery,
- setAddelivery,
- accountCreateLogs,
- setAccountCreateLogs,
- materialData,
- setMaterialData,
- textData,
- setTextData,
- clearData
- }}>
- <div className={style.settingsBody_content_right}>
- {/* 广告信息 */}
- <Ad />
- {/* 定向 */}
- <Target />
- {/* 创意 */}
- <Dynamic
- creativeTemplateAppellation={creativeTemplateAppellation}
- creativeTemplateStyle={creativeTemplateStyle}
- />
- {/* 创意素材 */}
- <Material />
- </div>
- <div className={style.settingsBody_content_left}>
- {/* 创意文案 */}
- <MaterialText />
- {/* 落地页 */}
- <PageList />
- </div>
- </DispatchAddelivery.Provider>
- </div>
- </div>
- <Space className={style.bts} wrap>
- <Save addelivery={addelivery} />
- <Button type='primary' onClick={severBd}>存为预设</Button>
- <Popconfirm
- title="确定清空?"
- onConfirm={delBdPlan}
- >
- <Button>清空配置/预设</Button>
- </Popconfirm>
- <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
- </Space>
- {/* 选择小说 */}
- {goodsVisible && <GoodsModal
- marketingTargetType={marketingAssetOuterSpec?.marketingTargetType}
- visible={goodsVisible}
- data={accountCreateLogs}
- onClose={() => setGoodsVisible(false)}
- onChange={(e) => {
- setAccountCreateLogs(e);
- setGoodsVisible(false);
- clearData()
- }}
- />}
- {/* 选择数据源 */}
- {sourceVisible && <DataSourceModal
- visible={sourceVisible}
- data={accountCreateLogs}
- onClose={() => setSourceVisible(false)}
- onChange={(e) => {
- setAccountCreateLogs(e);
- setSourceVisible(false);
- clearData()
- }}
- />}
- {/* 选择公众号 */}
- {wechatVisible && <WechatAccount
- visible={wechatVisible}
- data={accountCreateLogs}
- onClose={() => setWechatVisible(false)}
- onChange={(e) => {
- setAccountCreateLogs(e);
- setWechatVisible(false);
- clearData()
- }}
- />}
- {/* 选择视频号 */}
- {channelsProfileVisible && <VideoChannel
- visible={channelsProfileVisible}
- data={accountCreateLogs}
- onClose={() => setChannelsProfileVisible(false)}
- onChange={(e) => {
- setAccountCreateLogs(e);
- setChannelsProfileVisible(false);
- clearData()
- }}
- />}
- {/* 转化归因 */}
- {conversionVisible && <ConversionSelect
- adgroups={addelivery.adgroups}
- visible={conversionVisible}
- data={accountCreateLogs}
- onClose={() => setConversionVisible(false)}
- onChange={(e) => {
- setAccountCreateLogs(e);
- setConversionVisible(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>广告总数:{adCount}</span>
- <span>创意总数:{dynamicCount}</span>
- <Button type='primary' onClick={() => {
- setSubVisible(true)
- }}>提交创建</Button>
- </Space>}
- >
- {accountCreateLogs.map(item => <Tabs.TabPane tab={item.accountId} key={item.accountId} />)}
- </Tabs>
- {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
- <div className={style.content} style={{ marginTop: 20 }}>
- <Table
- columns={columns()}
- dataSource={tableData[activeKey]}
- size="small"
- bordered
- scroll={{ x: 1200, y: 600 }}
- rowKey={'id'}
- pagination={{
- defaultPageSize: 50,
- total: tableData[activeKey]?.length || 0,
- showTotal: (total) => <Tag color="cyan">当前共{total}个创意,{tableData[activeKey]?.[0]?.adLength}个广告</Tag>,
- }}
- />
- </div>
- </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
- <Empty description="请先完成模块配置后,再预览广告计划" />
- </div>}
- </Card>
- {/* 提交任务 */}
- {subVisible && <SubmitModal
- ajax={createAdgroupTask}
- visible={subVisible}
- onChange={(e) => {
- onSubmit(e)
- }}
- onClose={() => {
- setSubVisible(false)
- }}
- />}
- </Space>
- }
- export default Create
|