index.tsx 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. import { Button, Card, Empty, Modal, Popconfirm, Radio, Space, Spin, Table, Tabs, Tag, message, notification } from "antd"
  2. import React, { useEffect, useState } from "react"
  3. import style from './index.less'
  4. import '../index.less'
  5. import { CheckOutlined, SearchOutlined } from "@ant-design/icons"
  6. import Ad from "./Ad"
  7. import Target from "./Target"
  8. import { useAjax } from "@/Hook/useAjax"
  9. import { useModel } from "umi"
  10. import GoodsModal from "../../components/GoodsModal"
  11. import DataSourceModal from "../../components/DataSourceModal"
  12. import moment from "moment"
  13. import Dynamic from "./Dynamic"
  14. import Material from "./Material"
  15. import MaterialText from "./MaterialText"
  16. import PageList from "./PageList"
  17. import { cartesianProduct, distributeArray, processData, randomString, splitArrayIntoRandomChunks } from "@/utils/utils"
  18. import columns from "./tableConfig"
  19. import SubmitModal from "./submitModal"
  20. import { createAdgroupTaskApi, getSelectTaskDetailApi } from "@/services/adqV3"
  21. import WechatAccount from "../../components/WechatAccount"
  22. import Title from "antd/lib/typography/Title"
  23. import { getCreativeDetailsApi } from "@/services/adqV3/global"
  24. import ConversionSelect from "../../components/ConversionSelect"
  25. import VideoChannel from "../../components/VideoChannel"
  26. import Save from "./Save"
  27. import { useLocalStorageState } from "ahooks"
  28. import SelectAccount from "./SelectAccount"
  29. export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
  30. /**
  31. * 创建广告
  32. * @returns
  33. */
  34. const Create: React.FC = () => {
  35. /*******************************************/
  36. const { initialState } = useModel('@@initialState');
  37. const { initTargeting } = useModel('useLaunchV3.useTargeting')
  38. const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
  39. const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, marketingSubGoal, siteSet, automaticSiteEnabled, sceneSpec, isConversion } = addelivery.adgroups
  40. const { deliveryMode, creativeTemplateId, creativeComponents } = addelivery.dynamic
  41. const [accountCreateLogs, setAccountCreateLogs] = useState<PULLIN.AccountCreateLogsProps[]>([]) // 账户
  42. const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择小说弹窗控制
  43. const [wechatVisible, setWechatVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
  44. const [channelsProfileVisible, setChannelsProfileVisible] = useState<boolean>(false) // 选择微信公众号弹窗控制
  45. const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
  46. const [conversionVisible, setConversionVisible] = useState<boolean>(false) // 选择转化归因控制
  47. const [materialData, setMaterialData] = useState<any>({}) // 素材数据
  48. const [textData, setTextData] = useState<any>({})
  49. const [tableData, setTableData] = useState<any>({})
  50. const [activeKey, setActiveKey] = useState<string>()
  51. const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
  52. const [adCount, setAdCount] = useState<number>(0)
  53. const [dynamicCount, setDynamicCount] = useState<number>(0)
  54. const [creativeTemplateAppellation, setCreativeTemplateAppellation] = useState<string>()
  55. const [creativeTemplateStyle, setCreativeTemplateStyle] = useState<string>()
  56. const [putInTypeList, setPutInTypeList] = useState<{ label: string, value: string }[]>([])
  57. const [putInType, setPutInType] = useLocalStorageState<'NOVEL' | 'GAME'>('PUTINTYPE');
  58. const [copyTask, setCopyTask] = useState<{ copyTaskId?: number, uuid?: string }>({})
  59. const [ownerAccountId, setOwnerAccountId] = useState<number>()
  60. const createAdgroupTask = useAjax((params) => createAdgroupTaskApi(params))
  61. const getSelectTaskDetail = useAjax((params) => getSelectTaskDetailApi(params))
  62. const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
  63. /*******************************************/
  64. // 判断游戏还是小说短剧
  65. useEffect(() => {
  66. let newputInTypeList: { label: string, value: string }[] = []
  67. newputInTypeList.push({ label: '小说/短剧投放', value: 'NOVEL' })
  68. newputInTypeList.push({ label: '游戏投放', value: 'GAME' })
  69. setPutInTypeList(newputInTypeList);
  70. newputInTypeList.length > 0 && setPutInType(type => (type && newputInTypeList.some(item => item.value === type) ? type : newputInTypeList[0].value) as 'NOVEL' | 'GAME');
  71. }, [initialState?.menu])
  72. useEffect(() => {
  73. initTargeting()
  74. }, [])
  75. useEffect(() => {
  76. if (creativeTemplateId) {
  77. let params: any = {
  78. marketingGoal,
  79. marketingTargetType: marketingAssetOuterSpec.marketingTargetType,
  80. marketingCarrierType,
  81. deliveryMode,
  82. creativeTemplateId,
  83. wechatSceneSpecPosition: sceneSpec?.wechatPosition,
  84. dynamicCreativeType: deliveryMode === 'DELIVERY_MODE_COMPONENT' ? 'DYNAMIC_CREATIVE_TYPE_PROGRAM' : 'DYNAMIC_CREATIVE_TYPE_COMMON'
  85. }
  86. if (automaticSiteEnabled) {
  87. params.automaticSiteEnabled = automaticSiteEnabled
  88. } else {
  89. params.siteSet = siteSet
  90. }
  91. params.taskType = putInType
  92. if (putInType === 'GAME') {
  93. params.marketingSubGoal = marketingSubGoal
  94. }
  95. getCreativeDetails.run(params).then(res => {
  96. if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
  97. let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
  98. setCreativeTemplateAppellation(adcreativeTemplateStructAdpermits.creativeTemplateAppellation)
  99. setCreativeTemplateStyle(adcreativeTemplateStructAdpermits.creativeTemplateStyle)
  100. let creativeComponents = adcreativeTemplateStructAdpermits?.creativeComponents || []
  101. let result = processData(creativeComponents);
  102. let newMaterialData: any = {};
  103. let newTextData: any = {};
  104. Object.keys(result).forEach(key => {
  105. let data = result[key]
  106. if ((key === 'image_list' || key === 'short_video' || key === 'video' || key === 'image' || key === 'element_story') && data.required) {
  107. newMaterialData[key] = data
  108. } else if (key === 'title' || (data.required && key === 'description')) {
  109. newTextData[key] = data
  110. }
  111. })
  112. setMaterialData(newMaterialData)
  113. setTextData(newTextData)
  114. }
  115. })
  116. }
  117. }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled, putInType])
  118. /** 存为预设 */
  119. const severBd = () => {
  120. if (putInType) {
  121. localStorage.setItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3', JSON.stringify({
  122. addelivery,
  123. accountCreateLogs,
  124. materialData,
  125. textData,
  126. putInType
  127. }))
  128. message.success('存储成功')
  129. }
  130. }
  131. /** 清除 */
  132. const delBdPlan = ({ isRemoveItem = true }: { isRemoveItem?: boolean }) => {
  133. if (isRemoveItem) {
  134. localStorage.removeItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3')
  135. }
  136. setAccountCreateLogs([])
  137. setMaterialData({})
  138. setTextData({})
  139. setAddelivery({
  140. adgroups: {},
  141. targeting: [],
  142. dynamic: {},
  143. dynamicMaterialDTos: {},
  144. dynamicCreativesTextDTOS: {},
  145. mediaType: 0
  146. })
  147. setCopyTask({})
  148. setTableData({})
  149. }
  150. /**数据回填 */
  151. useEffect(() => {
  152. if (putInTypeList?.length > 0) {
  153. let taskId = sessionStorage.getItem('TASKID3.0')
  154. let adqAdData = localStorage.getItem(putInType === 'GAME' ? 'ADQADV3_GAME' : 'ADQADV3')
  155. if (taskId) {
  156. getSelectTaskDetail.run(taskId).then(res => {
  157. if (res) {
  158. const { adgroupDTO, accountIdParamVOMap, targetings, dynamicCreativesDTO: { mediaType, ...dynamic }, dynamicCreativesTextDTO, dynamicMaterialDTOS, taskType = 'NOVEL', id, uuid } = res
  159. setCopyTask({ copyTaskId: id, uuid })
  160. if (putInTypeList.some(item => item.value === taskType)) {
  161. setPutInType(() => taskType)
  162. let beginDate = adgroupDTO.beginDate
  163. let endDate = adgroupDTO.endDate
  164. if (adgroupDTO?.deepConversionSpec) {
  165. adgroupDTO.depthConversionEnabled = true
  166. } else {
  167. adgroupDTO.depthConversionEnabled = false
  168. }
  169. if (beginDate && moment(beginDate) < moment()) {
  170. beginDate = moment().format('YYYY-MM-DD')
  171. endDate = moment().add(7, 'day').format('YYYY-MM-DD')
  172. message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
  173. }
  174. let dynamicGroup: any[] = []
  175. if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
  176. dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
  177. let { type, valueJson } = item[0]
  178. let value = JSON.parse(valueJson).value
  179. if (type === 'image') {
  180. return { image_id: { id: value.imageId, url: value.imageUrl, materialType: value.materialType, accountId: value?.accountId } }
  181. } else if (type === 'image_list' || type === 'element_story') {
  182. return { [type]: value.list.map((l: { imageUrl: any; imageId: any; materialType: any, accountId: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType, accountId: l?.accountId })) }
  183. } else if (type === 'short_video' || type === 'video') {
  184. let field = type === 'video' ? 'video_id' : 'short_video1'
  185. let videoData: any = {}
  186. videoData[field] = { materialType: value.materialType, url: value.videoUrl, id: value.videoId, keyFrameImageUrl: value?.keyFrameImageUrl, accountId: value?.accountId }
  187. if (value.imageUrl) {
  188. videoData['cover_id'] = { materialType: value.materialCoverType, url: value.imageUrl, id: value.iamgeId, accountId: value?.accountId }
  189. }
  190. return videoData
  191. } else {
  192. return {}
  193. }
  194. })
  195. } else { // 组件化创意
  196. dynamicGroup = dynamicMaterialDTOS?.map((item: any[]) => {
  197. return {
  198. list: item?.map((i: any) => {
  199. let { type, valueJson } = i
  200. let value = JSON.parse(valueJson).value
  201. if (type === 'image') {
  202. return { id: value.imageId, url: value.imageUrl, materialType: value.materialType, accountId: value?.accountId }
  203. } else if (type === 'image_list') {
  204. return value.list.map((l: { imageUrl: any; imageId: any; materialType: any, accountId: any }) => ({ url: l.imageUrl, id: l.imageId, materialType: l.materialType, accountId: l?.accountId }))
  205. } else if (type === 'video') {
  206. return { materialType: value.materialType, url: value.videoUrl, id: value.videoId, keyFrameImageUrl: value?.keyFrameImageUrl, accountId: value?.accountId }
  207. } else {
  208. return {}
  209. }
  210. })
  211. }
  212. })
  213. }
  214. let isConversion = false
  215. setAccountCreateLogs(Object.keys(accountIdParamVOMap || {}).map(accountId => {
  216. const { productDTOS, wechatOfficialAccountsVO, pageList, landingPageVOS, userActionSetsList, conversionInfo, wechatChannelVO, wechatAppletList } = accountIdParamVOMap[accountId]
  217. let data: PULLIN.AccountCreateLogsProps = {
  218. accountId: Number(accountId),
  219. productList: productDTOS
  220. }
  221. if (wechatOfficialAccountsVO) {
  222. data.wechatChannelList = [wechatOfficialAccountsVO]
  223. }
  224. if (pageList || landingPageVOS || wechatAppletList) {
  225. data.pageList = pageList || landingPageVOS || wechatAppletList?.map((item: { appletName: any; id: any }) => ({ ...item, pageName: item.appletName, pageId: item.id }))
  226. }
  227. if (userActionSetsList) {
  228. data.userActionSetsList = userActionSetsList
  229. }
  230. if (conversionInfo) {
  231. isConversion = true
  232. data.newConversionList = [conversionInfo]
  233. }
  234. if (wechatChannelVO) {
  235. data.videoChannelList = [wechatChannelVO]
  236. }
  237. return data
  238. }))
  239. setAddelivery({
  240. adgroups: { ...adgroupDTO, isConversion, adgroupName: adgroupDTO.adgroupName + '_副本' + randomString(true, 3, 5), endDate, beginDate },
  241. targeting: targetings.map((item: any) => {
  242. const { targetingName, ...targeting } = item
  243. return { targetingName, targeting }
  244. }),
  245. dynamic,
  246. dynamicMaterialDTos: dynamicGroup.length > 0 ? { dynamicGroup } : {},
  247. dynamicCreativesTextDTOS: dynamicCreativesTextDTO,
  248. mediaType: mediaType || 0
  249. })
  250. } else {
  251. message.error(`没有${taskType === 'NOVEL' ? '小说/短剧' : taskType === 'GAME' ? '游戏' : taskType}投放权限`)
  252. }
  253. sessionStorage.removeItem('TASKID3.0')
  254. }
  255. })
  256. } else if (adqAdData) {
  257. const { addelivery, accountCreateLogs, materialData, textData } = JSON.parse(adqAdData)
  258. if (addelivery?.adgroups) {
  259. if (addelivery?.adgroups?.beginDate && moment(addelivery?.adgroups?.beginDate) < moment()) {
  260. addelivery.adgroups.beginDate = moment().format('YYYY-MM-DD')
  261. message.warning('请注意,检测投放开始日期小于今天,已自动改成今天,如需修改,请重新设置')
  262. }
  263. if (addelivery?.adgroups?.endDate && moment(addelivery?.adgroups?.endDate) < moment()) {
  264. addelivery.adgroups.endDate = moment().format('YYYY-MM-DD')
  265. message.warning('请注意,检测投放结束日期小于今天,已自动改成今天,如需修改,请重新设置')
  266. }
  267. }
  268. setAddelivery({ ...addelivery })
  269. setAccountCreateLogs(accountCreateLogs)
  270. setMaterialData(materialData)
  271. setTextData(textData)
  272. }
  273. }
  274. }, [putInType, putInTypeList])
  275. // 预览
  276. const preview = () => {
  277. if (accountCreateLogs?.length === 0) {
  278. message.error('请先选择媒体账户')
  279. return
  280. }
  281. const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = addelivery
  282. if (["OPTIMIZATIONGOAL_FOLLOW", "OPTIMIZATIONGOAL_PAGE_SCAN_CODE"].includes(adgroups?.optimizationGoal)) {
  283. if (!adgroups?.depthConversionEnabled) {
  284. message.error('应公司要求,”关注和加企微“请开启深度转化优化')
  285. return
  286. }
  287. if (adgroups?.deepConversionSpec?.deepConversionWorthSpec?.expectedRoi < 0.03) {
  288. notification.warning({
  289. message: '请注意',
  290. description: `当前ROI出价小于0.03,当前${adgroups?.deepConversionSpec?.deepConversionWorthSpec?.expectedRoi}`
  291. })
  292. }
  293. }
  294. if (!(adgroups && Object.keys(adgroups).length)) {
  295. message.error('请先配置广告信息')
  296. return
  297. }
  298. if (!(targeting?.length)) {
  299. message.error('请先添加定向')
  300. return
  301. }
  302. if (!(dynamic && Object.keys(dynamic).length)) {
  303. message.error('请先配置创意')
  304. return
  305. }
  306. if ((materialData && Object.keys(materialData).length) && !(dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
  307. message.error('请先配置创意素材')
  308. return
  309. }
  310. if ((textData && Object.keys(textData).length) && !(dynamicCreativesTextDTOS && Object.keys(dynamicCreativesTextDTOS).length)) {
  311. message.error('请先配置创意文案')
  312. return
  313. }
  314. if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType) && !accountCreateLogs?.some(item => item?.productList?.length)) {
  315. message.error(marketingAssetOuterSpec?.marketingTargetType === 'MARKETING_TARGET_TYPE_FICTION' ? '请先选择小说' : '请先选择短剧')
  316. return
  317. }
  318. 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)) {
  319. message.error('请先选择公众号')
  320. return
  321. }
  322. if (creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE' && !accountCreateLogs?.some(item => item?.videoChannelList?.length)) {
  323. message.error('请先选择视频号')
  324. return
  325. }
  326. if (isConversion && !accountCreateLogs?.some(item => item?.newConversionList?.length)) {
  327. message.error('请先选择转化归因')
  328. return
  329. }
  330. if (!accountCreateLogs?.some(item => item?.pageList?.length) && !['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
  331. message.error('请先选择落地页')
  332. return
  333. }
  334. let newTableData: any = {}
  335. let newAdCount = 0, newdynamicCount = 0
  336. const textType = dynamicCreativesTextDTOS.type
  337. const textDto = dynamicCreativesTextDTOS?.dynamicCreativesTextDetailDTOList || []
  338. const textDtoLenth = textDto.length
  339. const dynamicGroupLength = dynamicMaterialDTos?.dynamicGroup?.length || 0
  340. let newDynamicGroup: any = []
  341. if (![910].includes(dynamic.creativeTemplateId)) {
  342. newDynamicGroup = dynamicMaterialDTos?.dynamicGroup || []
  343. if (newDynamicGroup.length > 0 && [0, 1, 2, 3, 4].includes(textType)) {
  344. if (textType === 0) {
  345. newDynamicGroup = newDynamicGroup.map((item: any) => ({ ...item, textDto: textDto?.[0] }))
  346. } else if (textType === 1) {
  347. newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index] }))
  348. } else if (textType === 2) {
  349. newDynamicGroup = newDynamicGroup.map((item: any, index: number) => ({ ...item, textDto: textDto?.[index % textDtoLenth] }))
  350. } else if ((textType === 3 && mediaType === 0) || (textType === 4 && mediaType === 1) || (textType === 4 && mediaType === 3)) {
  351. newDynamicGroup = cartesianProduct(newDynamicGroup, textDto || [{}]).map((item) => {
  352. let [dynamicGroup, textDtoData] = item
  353. return {
  354. ...dynamicGroup as any,
  355. textDto: textDtoData
  356. }
  357. })
  358. }
  359. }
  360. }
  361. // 创意组平均分配到广告逻辑
  362. let averageAdDynamicList: any[] = []
  363. if ((mediaType === 1 || mediaType === 2 || mediaType === 3) && newDynamicGroup.length) {
  364. let adLength = 0
  365. accountCreateLogs.forEach(item => {
  366. let productList: any[] = []
  367. if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说 短剧
  368. productList = item?.productList || []
  369. } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
  370. productList = item?.wechatChannelList || []
  371. } else {
  372. productList = [{}]
  373. }
  374. adLength += productList.length * targeting.length
  375. })
  376. if (mediaType === 1) {
  377. if (textType === 4) {
  378. if (adLength > newDynamicGroup.length) {
  379. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${newDynamicGroup.length},广告数量:${adLength}`)
  380. return
  381. }
  382. averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
  383. } else {
  384. if (adLength > dynamicGroupLength) {
  385. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  386. return
  387. }
  388. averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
  389. }
  390. } else if (mediaType === 2) {
  391. if (adLength < dynamicGroupLength) {
  392. message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  393. return
  394. }
  395. } else if (mediaType === 3) {
  396. const dynamicDataLength = textType === 4 ? newDynamicGroup.length : dynamicGroupLength
  397. if (accountCreateLogs.some(item => {
  398. let productList: any[] = []
  399. if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说 短剧
  400. productList = item?.productList || []
  401. } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
  402. productList = item?.wechatChannelList || []
  403. } else {
  404. productList = [{}]
  405. }
  406. const adLength = productList.length * targeting.length
  407. if (adLength > dynamicDataLength) {
  408. message.error(`创意组分配规则选择“账号下平均分配到广告”时,创意组总数必须大于等于每个账号广告总数。当前创意组数量:${dynamicDataLength},当前账号(${item.accountId})下广告数量:${adLength}`)
  409. return true
  410. }
  411. return false
  412. })) {
  413. return
  414. }
  415. }
  416. }
  417. if (textType === 1) {
  418. if (dynamicGroupLength !== textDtoLenth) {
  419. message.error(`当前创意文案是“创意组一一对应”模式,创意组总数(${dynamicGroupLength})要等于创意文案总数(${textDtoLenth})`)
  420. return
  421. }
  422. if (!dynamicCreativesTextDTOS.dynamicCreativesTextDetailDTOList.every((item: {}) => item && Object.keys(item).length)) {
  423. message.error('创意文案配置错误,内容空')
  424. return
  425. }
  426. }
  427. // 落地页平均分配判断数量
  428. if ([910].includes(dynamic.creativeTemplateId) && dynamic?.landingPageType === 1) {
  429. let targetingLength = targeting.length
  430. if (accountCreateLogs.some(item => {
  431. let productListLength = item?.productList?.length || 1
  432. let total = targetingLength * productListLength
  433. let pageLength = item.pageList.length
  434. if (total > pageLength) {
  435. message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
  436. return true
  437. }
  438. return false
  439. })) {
  440. return
  441. }
  442. }
  443. let accountIndex = 0, accountIndex1 = 0
  444. accountCreateLogs.forEach(item => {
  445. let productList: any[] = [{}]
  446. if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
  447. productList = item?.productList || []
  448. } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
  449. productList = item?.wechatChannelList || []
  450. }
  451. if (mediaType === 3) {
  452. const adLength = productList.length * targeting.length
  453. if (textType === 4) {
  454. averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
  455. } else {
  456. averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
  457. }
  458. }
  459. let data = cartesianProduct(productList, targeting).map((newD, dindex) => {
  460. let [productDto, targetDto, index] = newD
  461. let suffix = '_' + item.accountId + '_' + index
  462. let averageAdDynamic = mediaType === 3 ? averageAdDynamicList?.[dindex] : averageAdDynamicList?.[accountIndex]
  463. let dat: any = {
  464. id: item.accountId + '_' + index,
  465. accountId: item.accountId, // 账户
  466. userActionSetsList: item.userActionSetsList, // 数据源
  467. conversionList: item.newConversionList, // 转化归因
  468. pageListDto: item.pageList, // 落地页
  469. productDto, // 商品
  470. targetDto: { // 定向
  471. ...targetDto,
  472. targetingName: targetDto.targetingName + suffix
  473. },
  474. adgroupsDto: { // 广告信息
  475. ...adgroups,
  476. adgroupName: adgroups.adgroupName + suffix
  477. },
  478. dynamicDto: dynamic, // 创意信息
  479. averageAdDynamic,
  480. rowSpan: ((mediaType === 1 || mediaType === 3) && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
  481. }
  482. if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
  483. dat.marketingCarrierDto = item?.wechatChannelList
  484. }
  485. accountIndex += 1
  486. return dat
  487. })
  488. newAdCount += data.length
  489. let newData: any[] = []
  490. // 激励
  491. if ([910].includes(dynamic.creativeTemplateId)) {
  492. if (dynamic?.landingPageType === 1) {
  493. let averageAdPageList: any[] = distributeArray(item.pageList, productList.length * targeting.length)
  494. data.forEach((item, aIndex) => {
  495. let aPageList: any[] = averageAdPageList[aIndex]
  496. aPageList.forEach((page, index) => {
  497. newData.push({
  498. ...item,
  499. id: item.id + '_' + index,
  500. pageListDto: [page],
  501. dynamicDto: {
  502. ...item.dynamicDto,
  503. dynamicCreativeName: item.dynamicDto.dynamicCreativeName + index
  504. },
  505. rowSpan: index === 0 ? aPageList.length : 0,
  506. adLength: data.length,
  507. isRowSpan: true
  508. })
  509. })
  510. })
  511. } else {
  512. newData = cartesianProduct(data, item.pageList).map((item, index) => {
  513. let [d1, pageList, num] = item
  514. return {
  515. ...d1,
  516. id: d1.id + '_' + index,
  517. pageListDto: [pageList],
  518. dynamicDto: {
  519. ...d1.dynamicDto,
  520. dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + num
  521. }
  522. }
  523. })
  524. }
  525. } else {
  526. if (mediaType === 1 || mediaType === 3) {
  527. data.forEach(item => {
  528. const { averageAdDynamic, ...ad } = item
  529. if (textType === 3) {
  530. let rowSpan = textDtoLenth * averageAdDynamic.length
  531. cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
  532. let [textValue, aad] = taad
  533. newData.push({
  534. ...ad,
  535. id: ad.id + '_' + index,
  536. dynamicGroup: aad,
  537. textDto: textValue,
  538. rowSpan: index === 0 ? rowSpan : 0,
  539. adLength: data.length,
  540. isRowSpan: true
  541. })
  542. })
  543. } else if (textType === 4) {
  544. averageAdDynamic.forEach((aad: any, index: number) => {
  545. newData.push({
  546. ...ad,
  547. id: ad.id + '_' + index,
  548. dynamicGroup: aad,
  549. textDto: aad?.textDto,
  550. adLength: data.length,
  551. rowSpan: index === 0 ? averageAdDynamic.length : 0,
  552. isRowSpan: true
  553. })
  554. })
  555. } else {
  556. averageAdDynamic.forEach((aad: any, index: number) => {
  557. newData.push({
  558. ...ad,
  559. id: ad.id + '_' + index,
  560. dynamicGroup: aad,
  561. textDto: textType === 2 ? textDto?.[index % textDtoLenth] : aad?.textDto,
  562. adLength: data.length,
  563. ...(mediaType === 3 ? {
  564. rowSpan: index === 0 ? averageAdDynamic.length : 0,
  565. isRowSpan: true
  566. } : {})
  567. })
  568. })
  569. }
  570. })
  571. } else if (mediaType === 2) {
  572. data.forEach((item) => {
  573. const { averageAdDynamic, ...ad } = item
  574. if (textType === 3) {
  575. cartesianProduct(textDto, [newDynamicGroup[accountIndex1 % newDynamicGroup.length]]).forEach((taad: any, i) => {
  576. let [textValue, aad, index] = taad
  577. newData.push({
  578. ...ad,
  579. id: ad.id + '_' + index,
  580. dynamicGroup: aad,
  581. textDto: textValue,
  582. rowSpan: i === 0 ? textDto.length : 0,
  583. adLength: data.length,
  584. isRowSpan: true
  585. })
  586. })
  587. } else {
  588. let { textDto: nowTextDto, ...dynamicGroup } = newDynamicGroup[accountIndex1 % newDynamicGroup.length]
  589. newData.push({
  590. ...ad,
  591. dynamicGroup,
  592. textDto: textType === 2 ? textDto?.[0] : nowTextDto,
  593. rowSpan: 1,
  594. adLength: data.length
  595. })
  596. }
  597. accountIndex1 += 1
  598. })
  599. } else { // 全账号复用创意组 所有广告一样的创意组
  600. newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
  601. let [d1, group] = item
  602. return {
  603. ...d1,
  604. id: d1.id + '_' + index,
  605. dynamicGroup: group,
  606. textDto: (group as any)?.textDto,
  607. adLength: data.length
  608. }
  609. })
  610. }
  611. }
  612. newdynamicCount = newdynamicCount + newData.length
  613. newTableData[item.accountId] = newData
  614. })
  615. setAdCount(newAdCount)
  616. setDynamicCount(newdynamicCount)
  617. setActiveKey(accountCreateLogs?.[0].accountId?.toString())
  618. console.log('newTableData-->', newTableData)
  619. setTableData(newTableData)
  620. }
  621. // 提交
  622. const onSubmit = (values: any) => {
  623. const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = JSON.parse(JSON.stringify(addelivery)) as PULLIN.AddeliveryProps
  624. let dynamicMaterialDTOS = []
  625. if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
  626. if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
  627. let mType = Object.keys(materialData)[0];
  628. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: any) => {
  629. if (mType === 'image') {
  630. return [{
  631. type: mType,
  632. valueJson: JSON.stringify({
  633. value: {
  634. imageUrl: item?.image_id?.url,
  635. imageId: item?.image_id?.id,
  636. materialType: item?.image_id?.materialType,
  637. accountId: item?.image_id?.accountId
  638. }
  639. })
  640. }]
  641. } else if (mType === 'image_list' || mType === 'element_story') {
  642. let key = 'image_list'
  643. if (mType === 'element_story') {
  644. key = 'element_story'
  645. }
  646. let list = item?.[key]?.map((l: any) => {
  647. return {
  648. imageUrl: l?.url,
  649. imageId: l?.id,
  650. materialType: l?.materialType,
  651. accountId: l?.accountId
  652. }
  653. })
  654. return [{
  655. type: mType,
  656. valueJson: JSON.stringify({
  657. value: {
  658. list
  659. }
  660. })
  661. }]
  662. } else if (['short_video', 'video'].includes(mType)) {
  663. let value: any = {
  664. materialType: item?.video_id?.materialType || item?.short_video1?.materialType || 0,
  665. videoUrl: item?.video_id?.url || item?.short_video1?.url,
  666. videoId: item?.video_id?.id || item?.short_video1?.id,
  667. keyFrameImageUrl: item?.video_id?.keyFrameImageUrl || item?.short_video1?.keyFrameImageUrl,
  668. accountId: item?.video_id?.accountId || item?.short_video1?.accountId,
  669. }
  670. if (item?.cover_id?.url) {
  671. value.imageUrl = item?.cover_id?.url
  672. value.imageId = item?.cover_id?.id
  673. value.materialCoverType = item?.cover_id?.materialType
  674. value.accountId = item?.cover_id?.accountId
  675. }
  676. return [{
  677. type: mType,
  678. valueJson: JSON.stringify({
  679. value
  680. })
  681. }]
  682. }
  683. return [{
  684. type: mType,
  685. valueJson: ''
  686. }]
  687. })
  688. }
  689. } else {
  690. if (dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length) {
  691. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: { list: any[] }) => {
  692. return item.list.map(l => {
  693. if (Array.isArray(l)) {
  694. return {
  695. type: 'image_list',
  696. valueJson: JSON.stringify({
  697. value: {
  698. list: l?.map((i: any) => {
  699. return {
  700. imageUrl: i?.url,
  701. imageId: i?.id,
  702. materialType: i?.materialType,
  703. accountId: i?.accountId
  704. }
  705. })
  706. }
  707. })
  708. }
  709. } else if (l?.url?.includes('mp4') || l?.keyFrameImageUrl) {
  710. return {
  711. type: 'video',
  712. valueJson: JSON.stringify({
  713. value: {
  714. materialType: l?.materialType,
  715. videoUrl: l?.url,
  716. videoId: l?.id,
  717. keyFrameImageUrl: l?.keyFrameImageUrl,
  718. accountId: l?.accountId
  719. }
  720. })
  721. }
  722. } else {
  723. return {
  724. type: 'image',
  725. valueJson: JSON.stringify({
  726. value: {
  727. imageUrl: l?.url,
  728. imageId: l?.id,
  729. materialType: l?.materialType,
  730. accountId: l?.accountId
  731. }
  732. })
  733. }
  734. }
  735. })
  736. })
  737. }
  738. }
  739. let accountIdParamDTOMap: any = {}
  740. accountCreateLogs.forEach(item => {
  741. let { pageList, productList, userActionSetsList, accountId, wechatChannelList, newConversionList, videoChannelList } = item
  742. let userActionSetsListDto = userActionSetsList?.map((item: any) => ({ id: item?.userActionSetId, type: item?.type })) // dataSourceId
  743. let map: any = {
  744. userActionSetsList: userActionSetsListDto,
  745. }
  746. if (!['PAGE_TYPE_WECHAT_MINI_GAME'].includes(dynamic?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType)) {
  747. map.pageList = pageList?.map((item: { pageId: any }) => item.pageId)
  748. }
  749. if (productList && ['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) {
  750. map.productDTOS = productList?.map(item => {
  751. return { productId: item.marketingAssetId }
  752. })
  753. }
  754. 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')) {
  755. map.wechatChannelId = wechatChannelList?.[0]?.wechatOfficialAccountId
  756. }
  757. if (newConversionList && isConversion) {
  758. map.conversionId = newConversionList?.[0]?.conversionId
  759. }
  760. if (videoChannelList && creativeComponents?.brand?.[0]?.value?.jumpInfo?.pageType === 'PAGE_TYPE_WECHAT_CHANNELS_PROFILE') {
  761. map.videoChannelId = videoChannelList?.[0]?.wechatChannelsAccountId
  762. }
  763. accountIdParamDTOMap[accountId] = map
  764. })
  765. let dynamicCreativesDTO = { ...dynamic, mediaType }
  766. if (dynamic.deliveryMode === 'DELIVERY_MODE_COMPONENT') {
  767. dynamicCreativesDTO.creativeTemplateId = 711
  768. }
  769. delete adgroups?.isConversion // 前端控制新链路字段
  770. let params = {
  771. ...values,
  772. adgroupDTO: adgroups,
  773. targetings: targeting.map(item => ({ targetingName: item.targetingName, ...item?.targeting || {} })),
  774. dynamicCreativesDTO,
  775. dynamicCreativesTextDTOS,
  776. dynamicMaterialDTOS,
  777. accountIdParamDTOMap,
  778. taskType: putInType,
  779. ...copyTask
  780. }
  781. createAdgroupTask.run(params).then(res => {
  782. if (res) {
  783. Modal.success({
  784. content: '任务提交成功',
  785. bodyStyle: { fontWeight: 700 },
  786. okText: '跳转任务列表',
  787. closable: true,
  788. onOk: () => {
  789. sessionStorage.setItem('CAMPV3', values?.taskName)
  790. window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
  791. },
  792. onCancel: () => {
  793. setSubVisible(false)
  794. }
  795. })
  796. }
  797. })
  798. }
  799. const clearData = () => {
  800. setTableData({})
  801. }
  802. return <Space direction="vertical" style={{ width: '100%' }}>
  803. <Spin spinning={createAdgroupTask.loading || getSelectTaskDetail.loading || getCreativeDetails.loading}>
  804. <Card
  805. size="small"
  806. title={<Space size={18}>
  807. <div className={style.cardTitle}>配置区</div>
  808. {putInTypeList.length >= 1 && <Radio.Group
  809. buttonStyle="solid"
  810. value={putInType}
  811. onChange={(e) => {
  812. setPutInType(e.target.value)
  813. delBdPlan({ isRemoveItem: false })
  814. }}
  815. options={putInTypeList}
  816. optionType='button'
  817. size="small"
  818. />}
  819. </Space>}
  820. className={style.createAd}
  821. >
  822. <Space wrap>
  823. <SelectAccount
  824. setAccountCreateLogs={setAccountCreateLogs}
  825. accountCreateLogs={accountCreateLogs}
  826. setOwnerAccountId={setOwnerAccountId}
  827. ownerAccountId={ownerAccountId}
  828. putInType={putInType}
  829. onChange={(e, isClear) => {
  830. clearData()
  831. setAccountCreateLogs(e?.map((item: any) => ({ accountId: item.accountId })) || [])
  832. if (isClear) {
  833. setAddelivery({ ...addelivery, dynamicMaterialDTos: {} })
  834. }
  835. }}
  836. dynamicGroup={addelivery?.dynamicMaterialDTos?.dynamicGroup}
  837. deliveryMode={deliveryMode}
  838. mType={Object.keys(materialData)[0]}
  839. />
  840. {accountCreateLogs?.length > 0 && <>
  841. {['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>}
  842. {(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>}
  843. {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>}
  844. {!isConversion ?
  845. <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: accountCreateLogs?.every(item => item?.userActionSetsList?.length) ? '#1890ff' : '#52C41A' }} />}</Button>
  846. :
  847. <Button type="primary" danger={!accountCreateLogs?.some(item => item?.newConversionList?.length)} onClick={() => { setConversionVisible(true) }}>{accountCreateLogs?.some(item => item?.newConversionList?.length) ? <>重新选择转化归因<CheckOutlined style={{ color: '#FFF' }} /></> : '请选择转化归因'}</Button>
  848. }
  849. </>}
  850. </Space>
  851. <div className={style.settingsBody}>
  852. <div className={style.settingsBody_content}>
  853. <DispatchAddelivery.Provider
  854. value={{
  855. addelivery,
  856. setAddelivery,
  857. accountCreateLogs,
  858. setAccountCreateLogs,
  859. materialData,
  860. setMaterialData,
  861. textData,
  862. setTextData,
  863. clearData,
  864. putInType,
  865. }}>
  866. <div className={style.settingsBody_content_right}>
  867. {/* 广告信息 */}
  868. <Ad />
  869. {/* 定向 */}
  870. <Target />
  871. {/* 创意 */}
  872. <Dynamic
  873. creativeTemplateAppellation={creativeTemplateAppellation}
  874. creativeTemplateStyle={creativeTemplateStyle}
  875. />
  876. {/* 创意素材 */}
  877. <Material />
  878. </div>
  879. <div className={style.settingsBody_content_left}>
  880. {/* 创意文案 */}
  881. <MaterialText />
  882. {/* 落地页 */}
  883. <PageList />
  884. </div>
  885. </DispatchAddelivery.Provider>
  886. </div>
  887. </div>
  888. <Space className={style.bts} wrap>
  889. <Save addelivery={addelivery} putInType={putInType} />
  890. <Button type='primary' onClick={severBd}>存为预设</Button>
  891. <Popconfirm
  892. title="确定清空?"
  893. onConfirm={() => delBdPlan({})}
  894. >
  895. <Button>清空配置/预设</Button>
  896. </Popconfirm>
  897. <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
  898. </Space>
  899. {/* 选择小说 */}
  900. {goodsVisible && <GoodsModal
  901. marketingTargetType={marketingAssetOuterSpec?.marketingTargetType}
  902. visible={goodsVisible}
  903. data={accountCreateLogs}
  904. onClose={() => setGoodsVisible(false)}
  905. onChange={(e) => {
  906. setAccountCreateLogs(e);
  907. setGoodsVisible(false);
  908. clearData()
  909. }}
  910. />}
  911. {/* 选择数据源 */}
  912. {sourceVisible && <DataSourceModal
  913. visible={sourceVisible}
  914. data={accountCreateLogs}
  915. onClose={() => setSourceVisible(false)}
  916. onChange={(e) => {
  917. setAccountCreateLogs(e);
  918. setSourceVisible(false);
  919. clearData()
  920. }}
  921. />}
  922. {/* 选择公众号 */}
  923. {wechatVisible && <WechatAccount
  924. visible={wechatVisible}
  925. data={accountCreateLogs}
  926. onClose={() => setWechatVisible(false)}
  927. onChange={(e) => {
  928. setAccountCreateLogs(e);
  929. setWechatVisible(false);
  930. clearData()
  931. }}
  932. />}
  933. {/* 选择视频号 */}
  934. {channelsProfileVisible && <VideoChannel
  935. visible={channelsProfileVisible}
  936. data={accountCreateLogs}
  937. onClose={() => setChannelsProfileVisible(false)}
  938. onChange={(e) => {
  939. setAccountCreateLogs(e);
  940. setChannelsProfileVisible(false);
  941. clearData()
  942. }}
  943. />}
  944. {/* 转化归因 */}
  945. {conversionVisible && <ConversionSelect
  946. adgroups={addelivery.adgroups}
  947. putInType={putInType}
  948. visible={conversionVisible}
  949. data={accountCreateLogs}
  950. onClose={() => setConversionVisible(false)}
  951. onChange={(e) => {
  952. setAccountCreateLogs(e);
  953. setConversionVisible(false);
  954. clearData()
  955. }}
  956. />}
  957. </Card>
  958. </Spin>
  959. <Card
  960. className={style.createAd}
  961. >
  962. {activeKey && tableData && Object.keys(tableData)?.length > 0 ? <div className={style.cardBody}>
  963. <Tabs
  964. onChange={(e) => { setActiveKey(e) }}
  965. type="card"
  966. activeKey={activeKey}
  967. tabBarExtraContent={<Space>
  968. <span>广告总数:{adCount}</span>
  969. <span>创意总数:{dynamicCount}</span>
  970. <Button type='primary' onClick={() => {
  971. setSubVisible(true)
  972. }}>提交创建</Button>
  973. </Space>}
  974. items={accountCreateLogs.map(item => ({ label: item.accountId, key: item.accountId?.toString() })) as any[]}
  975. />
  976. {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
  977. <div className={style.content} style={{ marginTop: 20 }}>
  978. <Table
  979. columns={columns()}
  980. dataSource={tableData[activeKey]}
  981. size="small"
  982. bordered
  983. scroll={{ x: 1200, y: 600 }}
  984. rowKey={'id'}
  985. pagination={{
  986. defaultPageSize: 50,
  987. total: tableData[activeKey]?.length || 0,
  988. showTotal: (total) => <Tag color="cyan">当前共{total}个创意,{tableData[activeKey]?.[0]?.adLength}个广告</Tag>,
  989. }}
  990. />
  991. </div>
  992. </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  993. <Empty description="请先完成模块配置后,再预览广告计划" />
  994. </div>}
  995. </Card>
  996. {/* 提交任务 */}
  997. {subVisible && <SubmitModal
  998. ajax={createAdgroupTask}
  999. visible={subVisible}
  1000. onChange={(e) => {
  1001. onSubmit(e)
  1002. }}
  1003. onClose={() => {
  1004. setSubVisible(false)
  1005. }}
  1006. />}
  1007. </Space>
  1008. }
  1009. export default Create