index.tsx 55 KB

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