12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037 |
- 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"
- 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 } = accountIdParamVOMap[accountId]
- let data: PULLIN.AccountCreateLogsProps = {
- accountId: Number(accountId),
- productList: productDTOS
- }
- if (wechatOfficialAccountsVO) {
- data.wechatChannelList = [wechatOfficialAccountsVO]
- }
- if (pageList || landingPageVOS) {
- data.pageList = pageList || landingPageVOS
- }
- 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 || []
- }
- 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) {
- map.productDTOS = productList?.map(item => {
- return { productId: item.marketingAssetId }
- })
- }
- if (wechatChannelList) {
- map.wechatChannelId = wechatChannelList?.[0]?.wechatOfficialAccountId
- }
- if (newConversionList) {
- map.conversionId = newConversionList?.[0]?.conversionId
- }
- if (videoChannelList) {
- 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>
- <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
|