index.tsx 56 KB

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