addDynamic.tsx 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. import { Button, Card, Drawer, Empty, Modal, Popconfirm, Space, Spin, Table, Tabs, Typography, message } from "antd"
  2. import React, { useEffect, useState } from "react"
  3. import '../index.less'
  4. import style from './index.less'
  5. import Dynamic from "./Dynamic";
  6. import Material from "./Material";
  7. import MaterialText from "./MaterialText";
  8. import PageList from "./PageList";
  9. import { DispatchAddelivery } from ".";
  10. import { CheckOutlined, SearchOutlined } from "@ant-design/icons";
  11. import WechatAccount from "../../components/WechatAccount";
  12. import { cartesianProduct, distributeArray, processData, splitArrayIntoRandomChunks, groupArr } from "@/utils/utils";
  13. import { columnsAddDynamic } from "./tableConfig";
  14. import { useAjax } from "@/Hook/useAjax";
  15. import { createDynamicTaskApi } from "@/services/adqV3";
  16. import TacticsS from "./TacticsS";
  17. import VideoChannel from "../../components/VideoChannel";
  18. import { getCreativeDetailsApi } from "@/services/adqV3/global";
  19. const { Text, Title } = Typography;
  20. /**
  21. * 新增创意
  22. * @returns
  23. */
  24. const AddDynamic: React.FC<PULLIN.NewAddDynamic> = ({ visible, onChange, onClose, adData: selectData, tactics, putInType }) => {
  25. /****************************************/
  26. const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
  27. const { adgroups, dynamic } = addelivery
  28. const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, marketingSubGoal, siteSet, automaticSiteEnabled, sceneSpec } = addelivery.adgroups
  29. const { deliveryMode, creativeTemplateId, dynamicCreativeSwitch } = addelivery.dynamic
  30. const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
  31. const [channelsProfileVisible, setChannelsProfileVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
  32. const [materialData, setMaterialData] = useState<any>({}) // 素材数据
  33. const [textData, setTextData] = useState<any>({})
  34. const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([]) // 账户
  35. const [tableData, setTableData] = useState<any>({})
  36. const [activeKey, setActiveKey] = useState<string>()
  37. const [dynamicCount, setDynamicCount] = useState<number>(0)
  38. const [adData, setAdData] = useState<any[]>(selectData)
  39. const [adLength, setAdLength] = useState<number>(0)
  40. const [adDataGroup, setAdDataGroup] = useState<{ [x: number]: any[] }>({})
  41. const [creativeTemplateAppellation, setCreativeTemplateAppellation] = useState<string>()
  42. const [creativeTemplateStyle, setCreativeTemplateStyle] = useState<string>()
  43. const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
  44. const createDynamicTask = useAjax((params) => createDynamicTaskApi(params))
  45. /****************************************/
  46. // 获取广告总数
  47. useEffect(() => {
  48. if (adData && adData?.length > 0) {
  49. setAdLength(adData.length)
  50. }
  51. }, [adData])
  52. useEffect(() => {
  53. if (creativeTemplateId) {
  54. let params: any = {
  55. marketingGoal,
  56. marketingTargetType: marketingAssetOuterSpec.marketingTargetType,
  57. marketingCarrierType,
  58. deliveryMode,
  59. creativeTemplateId,
  60. wechatSceneSpecPosition: sceneSpec?.wechatPosition,
  61. dynamicCreativeType: (deliveryMode === 'DELIVERY_MODE_COMPONENT' && !dynamicCreativeSwitch) ? 'DYNAMIC_CREATIVE_TYPE_PROGRAM' : 'DYNAMIC_CREATIVE_TYPE_COMMON'
  62. }
  63. if (automaticSiteEnabled) {
  64. params.automaticSiteEnabled = automaticSiteEnabled
  65. } else {
  66. params.siteSet = siteSet
  67. }
  68. params.taskType = putInType
  69. if (putInType === 'GAME') {
  70. params.marketingSubGoal = marketingSubGoal
  71. }
  72. getCreativeDetails.run(params).then(res => {
  73. if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
  74. let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
  75. setCreativeTemplateAppellation(adcreativeTemplateStructAdpermits.creativeTemplateAppellation)
  76. setCreativeTemplateStyle(adcreativeTemplateStructAdpermits.creativeTemplateStyle)
  77. let creativeComponents = adcreativeTemplateStructAdpermits?.creativeComponents || []
  78. let result = processData(creativeComponents);
  79. let newMaterialData: any = {};
  80. let newTextData: any = {};
  81. Object.keys(result).forEach(key => {
  82. let data = result[key]
  83. if ((key === 'image_list' || key === 'short_video' || key === 'video' || key === 'image' || key === 'element_story') && data.required) {
  84. newMaterialData[key] = data
  85. } else if (key === 'title' || (data.required && key === 'description')) {
  86. newTextData[key] = data
  87. }
  88. })
  89. setMaterialData(newMaterialData)
  90. setTextData(newTextData)
  91. }
  92. })
  93. }
  94. }, [creativeTemplateId, deliveryMode, dynamicCreativeSwitch, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled, putInType])
  95. useEffect(() => {
  96. if (adData && adData.length) {
  97. let adDataGroup: { [x: number]: any[] } = {}
  98. adData.forEach(item => {
  99. let accountId = item.accountId
  100. if (adDataGroup?.[accountId]?.length) {
  101. adDataGroup[accountId].push(item)
  102. } else {
  103. adDataGroup[accountId] = [item]
  104. }
  105. })
  106. setAdDataGroup(adDataGroup)
  107. }
  108. }, [adData])
  109. useEffect(() => {
  110. if (tactics) {
  111. const {
  112. adData,
  113. addelivery,
  114. accountCreateLogs,
  115. materialData,
  116. textData
  117. } = JSON.parse(tactics.strategyValue)
  118. setAccountCreateLogs(accountCreateLogs)
  119. if (addelivery?.adgroups) {
  120. if (addelivery?.adgroups?.smartDeliveryPlatform) {
  121. addelivery.adgroups.deliveryMethod = 'SMART'
  122. } else {
  123. addelivery.adgroups.deliveryMethod = 'NORMAL'
  124. }
  125. }
  126. setAddelivery(addelivery)
  127. setAdData(adData)
  128. setMaterialData(materialData)
  129. setTextData(textData)
  130. } else if (selectData?.length > 0) {
  131. const { siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled, marketingSubGoal, smartDeliveryPlatform } = selectData[0]
  132. let adgroups: Record<string, any> = {}
  133. if (smartDeliveryPlatform) {
  134. adgroups = { marketingGoal, marketingSubGoal, marketingCarrierType, automaticSiteEnabled: true, marketingAssetOuterSpec: { marketingTargetType }, deliveryMethod: 'SMART' }
  135. } else {
  136. adgroups = { marketingGoal, marketingSubGoal, marketingCarrierType, siteSet, automaticSiteEnabled, sceneSpec, marketingAssetOuterSpec: { marketingTargetType }, deliveryMethod: 'NORMAL' }
  137. }
  138. setAddelivery({ ...addelivery, adgroups })
  139. let AccountSet = new Set(selectData.map(item => item.accountId))
  140. setAccountCreateLogs([...AccountSet].map(accountId => ({ accountId })))
  141. }
  142. }, [selectData, tactics])
  143. const clearData = () => {
  144. setTableData([])
  145. }
  146. // 预览
  147. const preview = () => {
  148. if (accountCreateLogs?.length === 0) {
  149. message.error('请先选择媒体账户')
  150. return
  151. }
  152. const { adgroups, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
  153. if (!(adgroups && Object.keys(adgroups).length)) {
  154. message.error('请先配置广告信息')
  155. return
  156. }
  157. if (!(dynamic && Object.keys(dynamic).length)) {
  158. message.error('请先配置创意')
  159. return
  160. }
  161. if ((materialData && Object.keys(materialData).length) && !(dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
  162. message.error('请先配置创意素材')
  163. return
  164. }
  165. if ((textData && Object.keys(textData).length) && !(dynamicCreativesTextDTOS && Object.keys(dynamicCreativesTextDTOS).length)) {
  166. message.error('请先配置创意文案')
  167. return
  168. }
  169. if (!accountCreateLogs?.some(item => item?.pageList?.length) && !['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
  170. message.error('请先选择落地页')
  171. return
  172. }
  173. 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)) {
  174. message.error('请先选择公众号')
  175. return
  176. }
  177. if (dynamic?.creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE' && !accountCreateLogs?.some(item => item?.videoChannelList?.length)) {
  178. message.error('请先选择视频号')
  179. return
  180. }
  181. let newTableData: any = {}, newDynamicCount = 0
  182. const textType = dynamicCreativesTextDTOS.type
  183. const textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
  184. const textDtoLenth = textDto.length
  185. const dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
  186. let newDynamicGroup: any = []
  187. if (![910].includes(dynamic.creativeTemplateId)) {
  188. newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
  189. if (newDynamicGroup.length > 0 && [0, 1, 2, 3, 4].includes(textType)) {
  190. if (textType === 0) {
  191. newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
  192. } else if (textType === 1) {
  193. newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
  194. } else if (textType === 2) {
  195. newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
  196. } else if ((textType === 3 && mediaType === 0) || (textType === 4 && mediaType === 1) || (textType === 4 && mediaType === 3)) {
  197. newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
  198. let [dynamicGroup, textDtoData] = item
  199. return {
  200. ...dynamicGroup as any,
  201. textDto: textDtoData
  202. }
  203. })
  204. }
  205. }
  206. }
  207. // 创意组平均分配到广告逻辑
  208. let averageAdDynamicList: any[] = []
  209. if ((mediaType === 1 || mediaType === 2 || mediaType === 3) && newDynamicGroup.length) {
  210. const adLength = adData.length
  211. if (mediaType === 1) {
  212. if (textType === 4) {
  213. if (adLength > newDynamicGroup.length) {
  214. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  215. return
  216. }
  217. averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
  218. } else {
  219. if (adLength > dynamicGroupLength) {
  220. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  221. return
  222. }
  223. averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
  224. }
  225. } else if (mediaType === 2) {
  226. if (adLength < dynamicGroupLength) {
  227. message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  228. return
  229. }
  230. } else if (mediaType === 3) {
  231. const dynamicDataLength = textType === 4 ? newDynamicGroup.length : dynamicGroupLength
  232. if (Object.keys(adDataGroup).some(key => {
  233. const adLength = adDataGroup[key as any].length
  234. if (adLength > dynamicDataLength) {
  235. message.error(`创意组分配规则选择“账号下平均分配到广告”时,创意组总数必须大于等于每个账号广告总数。当前创意组数量:${dynamicDataLength},当前账号(${key})下广告数量:${adLength}`)
  236. return true
  237. }
  238. return false
  239. })) {
  240. return
  241. }
  242. }
  243. }
  244. const mainJumpInfoLength = dynamic?.creativeComponents?.mainJumpInfo?.length || 1
  245. // 落地页平均分配判断数量
  246. if ([910].includes(dynamic.creativeTemplateId) && dynamic?.landingPageType === 1) {
  247. if (accountCreateLogs.some(item => {
  248. const total = adDataGroup[item.accountId].length
  249. const pageLength = item.pageList.length
  250. if (mainJumpInfoLength > 1) {
  251. if (!(total * mainJumpInfoLength <= pageLength && pageLength % mainJumpInfoLength === 0)) {
  252. message.error(`当前${item.accountId}下的广告总数是:${total},落地页组数是:${mainJumpInfoLength},平均分配需要落地页总数(${pageLength})要大于${total * mainJumpInfoLength}并且要是${mainJumpInfoLength}倍数`)
  253. return true
  254. }
  255. } else {
  256. if (total > pageLength) {
  257. message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
  258. return true
  259. }
  260. }
  261. return false
  262. })) {
  263. return
  264. }
  265. } else if (textType === 5) {
  266. if (dynamicGroupLength * accountCreateLogs.length !== textDtoLenth) {
  267. message.error(`创意文案配置错误,内容数量和所有创意数量对不上,所有创意数量:${dynamicGroupLength * accountCreateLogs.length},文案数量:${textDtoLenth}`)
  268. return
  269. }
  270. }
  271. if (textType === 1) {
  272. if (dynamicGroupLength !== textDtoLenth) {
  273. message.error(`当前创意文案是“创意组一一对应”模式,创意组总数(${dynamicGroupLength})要等于创意文案总数(${textDtoLenth})`)
  274. return
  275. }
  276. if (!dynamicCreativesTextDTOS.dynamicCreativesTextDetailDTOList.every((item: {}) => item && Object.keys(item).length)) {
  277. message.error('创意文案配置错误,内容空')
  278. return
  279. }
  280. }
  281. let accountIndex1 = 0, accountIndex2 = 0
  282. if (dynamic?.landingPageType === 1 && [910].includes(dynamic.creativeTemplateId)) {
  283. accountCreateLogs.forEach(item => {
  284. const adData = adDataGroup[item.accountId]
  285. const averageAdPageList: any[] = groupArr(item.pageList, adData.length, mainJumpInfoLength)
  286. let newData: any[] = []
  287. adData.forEach((ad, index) => {
  288. const data = [{
  289. id: ad.adgroupId + '_' + index,
  290. adgroupsDto: ad,
  291. dynamicDto: dynamic, // 创意信息
  292. }]
  293. newData = cartesianProduct(data, averageAdPageList[index]).map((item, index) => {
  294. const [d1, pageList, num] = item
  295. return {
  296. ...d1,
  297. id: d1.id + '_' + index,
  298. pageListDto: pageList,
  299. dynamicDto: {
  300. ...d1.dynamicDto,
  301. dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + num
  302. },
  303. rowSpan: index === 0 ? averageAdPageList[index].length : 0,
  304. isRowSpan: true
  305. }
  306. })
  307. newTableData[ad.adgroupId] = newData
  308. })
  309. newDynamicCount += item.pageList.length
  310. })
  311. } else {
  312. const handleDynamic = (adData: any[], averageAdDynamicList: any[]) => {
  313. adData.forEach((ad, index) => {
  314. const item = accountCreateLogs.find(a => a.accountId === ad.accountId) as PULLIN.AccountCreateLogsProps
  315. const averageAdDynamic = averageAdDynamicList?.[index]
  316. const data = [{
  317. id: ad.adgroupId + '_' + index,
  318. pageListDto: item.pageList, // 落地页
  319. adgroupsDto: ad,
  320. dynamicDto: dynamic, // 创意信息
  321. averageAdDynamic,
  322. rowSpan: ((mediaType === 1 || mediaType === 3) && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length / mainJumpInfoLength : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
  323. }]
  324. let newData: any[] = []
  325. if ([910].includes(dynamic.creativeTemplateId)) {
  326. newData = cartesianProduct(data, distributeArray(item.pageList, item.pageList.length / mainJumpInfoLength)).map((item, index) => {
  327. const [d1, pageList, num] = item
  328. return {
  329. ...d1,
  330. id: d1.id + '_' + index,
  331. pageListDto: pageList,
  332. dynamicDto: {
  333. ...d1.dynamicDto,
  334. dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + num
  335. }
  336. }
  337. })
  338. } else {
  339. if (mediaType === 1 || mediaType === 3) {
  340. data.forEach(item => {
  341. const { averageAdDynamic, ...ad } = item
  342. if (textType === 3) {
  343. const rowSpan = textDtoLenth * averageAdDynamic.length
  344. cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
  345. const [textValue, aad] = taad
  346. newData.push({
  347. ...ad,
  348. id: ad.id + '_' + index,
  349. dynamicGroup: aad,
  350. textDto: textValue,
  351. rowSpan
  352. })
  353. })
  354. } else if (textType === 4) {
  355. averageAdDynamic.forEach((aad: any, index: number) => {
  356. newData.push({
  357. ...ad,
  358. id: ad.id + '_' + index,
  359. dynamicGroup: aad,
  360. textDto: aad?.textDto,
  361. rowSpan: index === 0 ? averageAdDynamic.length : 0,
  362. isRowSpan: true
  363. })
  364. })
  365. } else {
  366. averageAdDynamic.forEach((aad: any, index: number) => {
  367. newData.push({
  368. ...ad,
  369. id: ad.id + '_' + index,
  370. dynamicGroup: aad,
  371. textDto: textType === 2 ? textDto?.[index % textDtoLenth] : textType === 5 ? textDto?.[accountIndex2] : aad?.textDto
  372. })
  373. accountIndex2 += 1
  374. })
  375. }
  376. })
  377. } else if (mediaType === 2) {
  378. data.forEach((item) => {
  379. const { averageAdDynamic, ...ad } = item
  380. if (textType === 3) {
  381. cartesianProduct(textDto, [newDynamicGroup[accountIndex1 % newDynamicGroup.length]]).forEach((taad: any) => {
  382. let [textValue, aad, index] = taad
  383. newData.push({
  384. ...ad,
  385. id: ad.id + '_' + index,
  386. dynamicGroup: aad,
  387. textDto: textValue,
  388. rowSpan: textDto.length
  389. })
  390. })
  391. } else {
  392. let { textDto: nowTextDto, ...dynamicGroup } = newDynamicGroup[accountIndex1 % newDynamicGroup.length]
  393. newData.push({
  394. ...ad,
  395. dynamicGroup,
  396. textDto: textType === 2 ? textDto?.[0] : nowTextDto,
  397. rowSpan: 1
  398. })
  399. }
  400. accountIndex1 += 1
  401. })
  402. } else {
  403. newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
  404. let [d1, group] = item
  405. return {
  406. ...d1,
  407. id: d1.id + '_' + index,
  408. dynamicGroup: group,
  409. textDto: (group as any)?.textDto
  410. }
  411. })
  412. }
  413. }
  414. newDynamicCount += newData.length
  415. newTableData[ad.adgroupId] = newData
  416. })
  417. }
  418. if (mediaType === 3) {
  419. Object.keys(adDataGroup).forEach(key => {
  420. const newAdData = adDataGroup[key as any]
  421. const adLength = newAdData.length
  422. if (textType === 4) {
  423. averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
  424. } else {
  425. averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
  426. }
  427. handleDynamic(newAdData, averageAdDynamicList)
  428. })
  429. } else {
  430. handleDynamic(adData, averageAdDynamicList)
  431. }
  432. }
  433. setDynamicCount(newDynamicCount)
  434. setActiveKey(adData?.[0].adgroupId?.toString())
  435. console.log('newTableData-->', newTableData)
  436. setTableData(newTableData)
  437. }
  438. // 提交
  439. const onSubmit = () => {
  440. const { dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
  441. let dynamicMaterialDTOS = []
  442. if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE' || dynamic?.dynamicCreativeSwitch) {
  443. if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
  444. const mType = Object.keys(materialData)[0];
  445. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: any) => {
  446. if (mType === 'image') {
  447. return [{
  448. type: mType,
  449. valueJson: JSON.stringify({
  450. value: {
  451. imageUrl: item?.image_id?.url,
  452. imageId: item?.image_id?.id,
  453. materialType: item?.image_id?.materialType,
  454. accountId: item?.image_id?.accountId
  455. }
  456. })
  457. }]
  458. } else if (mType === 'image_list' || mType === 'element_story') {
  459. let key = 'image_list'
  460. if (mType === 'element_story') {
  461. key = 'element_story'
  462. }
  463. const list = item?.[key]?.map((l: any) => {
  464. return {
  465. imageUrl: l?.url,
  466. imageId: l?.id,
  467. materialType: l?.materialType,
  468. accountId: l?.accountId
  469. }
  470. })
  471. return [{
  472. type: mType,
  473. valueJson: JSON.stringify({
  474. value: {
  475. list
  476. }
  477. })
  478. }]
  479. } else if (['short_video', 'video'].includes(mType)) {
  480. const value: any = {
  481. materialType: item?.video_id?.materialType || item?.short_video1?.materialType || 0,
  482. videoUrl: item?.video_id?.url || item?.short_video1?.url,
  483. videoId: item?.video_id?.id || item?.short_video1?.id,
  484. keyFrameImageUrl: item?.video_id?.keyFrameImageUrl || item?.short_video1?.keyFrameImageUrl,
  485. accountId: item?.video_id?.accountId || item?.short_video1?.accountId,
  486. }
  487. if (item?.cover_id?.url) {
  488. value.imageUrl = item?.cover_id?.url
  489. value.imageId = item?.cover_id?.id
  490. value.materialCoverType = item?.cover_id?.materialType
  491. value.accountId = item?.cover_id?.accountId
  492. }
  493. return [{
  494. type: mType,
  495. valueJson: JSON.stringify({
  496. value
  497. })
  498. }]
  499. }
  500. return [{
  501. type: mType,
  502. valueJson: ''
  503. }]
  504. })
  505. }
  506. } else {
  507. if (dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length) {
  508. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: { list: any[] }) => {
  509. return item.list.map(l => {
  510. if (l.materialType === 4) {
  511. return {
  512. type: l?.componentSubType?.includes('IMAGE_LIST') ? 'image_list' : l?.keyFrameImageUrl ? 'video' : 'image',
  513. valueJson: JSON.stringify({
  514. componentId: l.id,
  515. componentValue: l
  516. })
  517. }
  518. }
  519. if (Array.isArray(l)) {
  520. return {
  521. type: 'image_list',
  522. valueJson: JSON.stringify({
  523. value: {
  524. list: l?.map((i: any) => {
  525. return {
  526. imageUrl: i?.url,
  527. imageId: i?.id,
  528. materialType: i?.materialType,
  529. accountId: i?.accountId
  530. }
  531. })
  532. }
  533. })
  534. }
  535. } else if (l?.url?.includes('mp4') || l?.keyFrameImageUrl) {
  536. return {
  537. type: 'video',
  538. valueJson: JSON.stringify({
  539. value: {
  540. materialType: l?.materialType,
  541. videoUrl: l?.url,
  542. videoId: l?.id,
  543. keyFrameImageUrl: l?.keyFrameImageUrl,
  544. accountId: l?.accountId
  545. }
  546. })
  547. }
  548. } else {
  549. return {
  550. type: 'image',
  551. valueJson: JSON.stringify({
  552. value: {
  553. imageUrl: l?.url,
  554. imageId: l?.id,
  555. materialType: l?.materialType,
  556. accountId: l?.accountId
  557. }
  558. })
  559. }
  560. }
  561. })
  562. })
  563. }
  564. }
  565. let accountIdParamDTOMap: any = {}
  566. accountCreateLogs.forEach(item => {
  567. const { pageList, userActionSetsList, accountId, wechatChannelList, videoChannelList } = item
  568. const userActionSetsListDto = userActionSetsList?.map((item: any) => ({ id: item?.userActionSetId, type: item?.type })) // dataSourceId
  569. const pageMap: { [x: string]: any } = {}
  570. const map: any = {
  571. userActionSetsList: userActionSetsListDto,
  572. pageList: pageList?.map((item: { pageId: any, pageName?: string }) => {
  573. pageMap[item.pageId] = item?.pageName
  574. return item.pageId
  575. })
  576. }
  577. if (Object.keys(pageMap).length) {
  578. map.pageMap = pageMap
  579. }
  580. if (wechatChannelList && (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(adgroups?.marketingAssetOuterSpec?.marketingTargetType) || adgroups?.marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT' || dynamic?.creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL')) {
  581. map.wechatChannelId = wechatChannelList?.[0]?.wechatOfficialAccountId
  582. }
  583. if (videoChannelList && dynamic?.creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE') {
  584. map.videoChannelId = videoChannelList?.[0]?.wechatChannelsAccountId
  585. }
  586. accountIdParamDTOMap[accountId] = map
  587. })
  588. dynamic.dynamicCreativeType = 'DYNAMIC_CREATIVE_TYPE_COMMON'
  589. if (dynamic.deliveryMode === 'DELIVERY_MODE_COMPONENT') {
  590. dynamic.dynamicCreativeType = dynamic?.dynamicCreativeSwitch ? 'DYNAMIC_CREATIVE_TYPE_COMMON' : 'DYNAMIC_CREATIVE_TYPE_PROGRAM'
  591. }
  592. let dynamicCreativesDTO = { ...dynamic, mediaType }
  593. if (dynamic.deliveryMode === 'DELIVERY_MODE_COMPONENT' && !dynamic?.dynamicCreativeSwitch) {
  594. dynamicCreativesDTO.creativeTemplateId = 711
  595. }
  596. let params = {
  597. accountAdgroupMaps: adData.map(item => `${item.accountId},${item.adgroupId}`),
  598. dynamicCreativesDTO,
  599. dynamicCreativesTextDTOS,
  600. dynamicMaterialDTOS,
  601. accountIdParamDTOMap
  602. }
  603. createDynamicTask.run(params).then(res => {
  604. if (res) {
  605. Modal.success({
  606. content: '创建任务提交成功',
  607. bodyStyle: { fontWeight: 700 },
  608. okText: '返回上一页',
  609. closable: true,
  610. onOk: () => {
  611. onChange?.()
  612. },
  613. onCancel: () => { }
  614. })
  615. }
  616. })
  617. }
  618. return <Drawer
  619. title={<strong>添加创意</strong>}
  620. open={visible}
  621. width={1500}
  622. onClose={onClose}
  623. bodyStyle={{ backgroundColor: '#f1f4fc', padding: '0 10px 10px' }}
  624. >
  625. <Space direction="vertical" style={{ width: '100%' }}>
  626. <Spin spinning={false}>
  627. <Card
  628. size="small"
  629. title={<div className={style.cardTitle}>配置区</div>}
  630. className={style.createAd}
  631. >
  632. <Space wrap>
  633. {(adgroups?.marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT' || adgroups?.marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT' || addelivery?.dynamic?.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>}
  634. {dynamic?.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>}
  635. </Space>
  636. <div className={style.settingsBody}>
  637. <div className={style.settingsBody_content}>
  638. <DispatchAddelivery.Provider
  639. value={{
  640. addelivery,
  641. setAddelivery,
  642. accountCreateLogs,
  643. setAccountCreateLogs,
  644. materialData,
  645. setMaterialData,
  646. textData,
  647. setTextData,
  648. clearData,
  649. putInType,
  650. adLength
  651. }}>
  652. <div className={style.settingsBody_content_right}>
  653. <div className={`${style.settingsBody_content_row} ${style.row1}`}>
  654. <div className={style.title}>
  655. <span>广告信息</span>
  656. </div>
  657. <div className={style.detail}>
  658. <div className={style.detail_body}>
  659. <Title level={5} style={{ fontSize: 12 }}>已选广告</Title>
  660. {Object.keys(adDataGroup).map((key: any) => {
  661. return <div key={key}>
  662. <Title level={5} style={{ fontSize: 12 }}>{key}</Title>
  663. {adDataGroup[key]?.map((item: any) => {
  664. return <div key={item.adgroupId}>
  665. <div className={style.text}><Text ellipsis={{ tooltip: true }}>{item.adgroupName}</Text></div>
  666. </div>
  667. })}
  668. </div>
  669. })}
  670. </div>
  671. </div>
  672. </div>
  673. {/* 创意 */}
  674. <Dynamic
  675. creativeTemplateAppellation={creativeTemplateAppellation}
  676. creativeTemplateStyle={creativeTemplateStyle}
  677. />
  678. {/* 创意素材 */}
  679. <Material adData={adData} />
  680. </div>
  681. <div className={style.settingsBody_content_left}>
  682. {/* 创意文案 */}
  683. <MaterialText adDataGroup={adDataGroup} />
  684. {/* 落地页 */}
  685. <PageList adDataGroup={adDataGroup} />
  686. </div>
  687. </DispatchAddelivery.Provider>
  688. </div>
  689. </div>
  690. <Space className={style.bts} wrap>
  691. <TacticsS
  692. strategyValue={{
  693. adData: adData.map(item => {
  694. const { accountId, adgroupName, adgroupId, siteSet, marketingCarrierType, marketingGoal, marketingTargetType, sceneSpec, automaticSiteEnabled, marketingSubGoal } = item
  695. return {
  696. siteSet, marketingCarrierType, marketingGoal, marketingSubGoal, marketingTargetType, sceneSpec, automaticSiteEnabled,
  697. accountId,
  698. adgroupId,
  699. adgroupName
  700. }
  701. }),
  702. addelivery,
  703. accountCreateLogs,
  704. materialData,
  705. textData
  706. }}
  707. putInType={putInType}
  708. />
  709. <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
  710. </Space>
  711. {/* 选择公众号 */}
  712. {wechatVisible && <WechatAccount
  713. visible={wechatVisible}
  714. data={accountCreateLogs}
  715. onClose={() => setWechatVisible(false)}
  716. onChange={(e) => {
  717. setAccountCreateLogs(e);
  718. setWechatVisible(false);
  719. clearData()
  720. }}
  721. />}
  722. {/* 选择视频号 */}
  723. {channelsProfileVisible && <VideoChannel
  724. visible={channelsProfileVisible}
  725. data={accountCreateLogs}
  726. onClose={() => setChannelsProfileVisible(false)}
  727. onChange={(e) => {
  728. setAccountCreateLogs(e);
  729. setChannelsProfileVisible(false);
  730. clearData()
  731. }}
  732. />}
  733. </Card>
  734. </Spin>
  735. <Card
  736. className={style.createAd}
  737. >
  738. {activeKey && tableData && Object.keys(tableData)?.length > 0 ? <div className={style.cardBody}>
  739. <Tabs
  740. onChange={(e) => { setActiveKey(e) }}
  741. type="card"
  742. activeKey={activeKey}
  743. tabBarExtraContent={<Space>
  744. <span>创意总数:{dynamicCount}</span>
  745. <Popconfirm
  746. title="确定提交?"
  747. onConfirm={onSubmit}
  748. >
  749. <Button type='primary' loading={createDynamicTask.loading}>提交创建</Button>
  750. </Popconfirm>
  751. </Space>}
  752. >
  753. {adData.map(item => <Tabs.TabPane tab={item.adgroupId} key={item.adgroupId} />)}
  754. </Tabs>
  755. {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
  756. <div className={style.content} style={{ marginTop: 20 }}>
  757. <Table
  758. columns={columnsAddDynamic()}
  759. dataSource={tableData[activeKey]}
  760. size="small"
  761. bordered
  762. scroll={{ x: 1200 }}
  763. rowKey={'id'}
  764. pagination={{
  765. defaultPageSize: 50
  766. }}
  767. />
  768. </div>
  769. </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  770. <Empty description="请先完成模块配置后,再预览广告计划" />
  771. </div>}
  772. </Card>
  773. </Space>
  774. </Drawer>
  775. }
  776. export default React.memo(AddDynamic)