index.tsx 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  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. export const DispatchAddelivery = React.createContext<PULLIN.DispatchAddelivery | null>(null);
  29. /**
  30. * 创建广告
  31. * @returns
  32. */
  33. const Create: React.FC = () => {
  34. /*******************************************/
  35. const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
  36. const { initTargeting } = useModel('useLaunchV3.useTargeting')
  37. const [addelivery, setAddelivery] = useState<PULLIN.AddeliveryProps>({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
  38. const { marketingAssetOuterSpec, marketingCarrierType, marketingGoal, siteSet, automaticSiteEnabled, sceneSpec, isConversion } = addelivery.adgroups
  39. const { deliveryMode, creativeTemplateId, creativeComponents } = addelivery.dynamic
  40. const [accSearch, setAccSearch] = useState<string>()
  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 getGroupList = useAjax(() => getGroupListApi())
  57. const createAdgroupTask = useAjax((params) => createAdgroupTaskApi(params))
  58. const getSelectTaskDetail = useAjax((params) => getSelectTaskDetailApi(params))
  59. const getCreativeDetails = useAjax((params) => getCreativeDetailsApi(params))
  60. /*******************************************/
  61. useEffect(() => {
  62. // 获取账户组
  63. getGroupList.run()
  64. // 获取账户列表
  65. getAllUserAccount.run()
  66. initTargeting()
  67. }, [])
  68. useEffect(() => {
  69. if (creativeTemplateId) {
  70. let params: any = {
  71. marketingGoal,
  72. marketingTargetType: marketingAssetOuterSpec.marketingTargetType,
  73. marketingCarrierType,
  74. deliveryMode,
  75. creativeTemplateId,
  76. wechatSceneSpecPosition: sceneSpec?.wechatPosition,
  77. dynamicCreativeType: deliveryMode === 'DELIVERY_MODE_COMPONENT' ? 'DYNAMIC_CREATIVE_TYPE_PROGRAM' : 'DYNAMIC_CREATIVE_TYPE_COMMON'
  78. }
  79. if (automaticSiteEnabled) {
  80. params.automaticSiteEnabled = automaticSiteEnabled
  81. } else {
  82. params.siteSet = siteSet
  83. }
  84. getCreativeDetails.run(params).then(res => {
  85. if (res?.adcreativeTemplateStructAdpermits?.length > 0) {
  86. let adcreativeTemplateStructAdpermits = res?.adcreativeTemplateStructAdpermits[0]
  87. setCreativeTemplateAppellation(adcreativeTemplateStructAdpermits.creativeTemplateAppellation)
  88. setCreativeTemplateStyle(adcreativeTemplateStructAdpermits.creativeTemplateStyle)
  89. let creativeComponents = adcreativeTemplateStructAdpermits?.creativeComponents || []
  90. let result = processData(creativeComponents);
  91. let newMaterialData: any = {};
  92. let newTextData: any = {};
  93. Object.keys(result).forEach(key => {
  94. let data = result[key]
  95. if ((key === 'image_list' || key === 'short_video' || key === 'video' || key === 'image' || key === 'element_story') && data.required) {
  96. newMaterialData[key] = data
  97. } else if (key === 'title' || (data.required && key === 'description')) {
  98. newTextData[key] = data
  99. }
  100. })
  101. setMaterialData(newMaterialData)
  102. setTextData(newTextData)
  103. }
  104. })
  105. }
  106. }, [creativeTemplateId, deliveryMode, marketingGoal, marketingAssetOuterSpec, marketingCarrierType, siteSet, sceneSpec?.wechatPosition, automaticSiteEnabled])
  107. /** 获取分组里账号 */
  108. const getGroupAccountList = (ids: number[]) => {
  109. if (ids.length > 0) {
  110. let data = ids.map(id => getAccountListApi(id))
  111. Promise.all(data).then(res => {
  112. if (res?.length > 0 && res.every((item: { code: number }) => item.code === 200)) {
  113. let userArr: any[] = []
  114. res.forEach((item: { data: { adAccountList: { accountId: number, id: number }[] } }) => {
  115. item.data.adAccountList.forEach(acc => {
  116. let obj = userArr.find((item: { accountId: number }) => item.accountId === acc.accountId)
  117. if (!obj) {
  118. userArr.push(acc)
  119. }
  120. })
  121. })
  122. setAccountCreateLogs(userArr?.map((item) => ({ accountId: item?.accountId })))
  123. clearData()
  124. setAddelivery({ adgroups: {}, targeting: [], dynamic: {}, dynamicMaterialDTos: {}, dynamicCreativesTextDTOS: {}, mediaType: 0 })
  125. } else {
  126. message.error('操作异常')
  127. }
  128. })
  129. } else {
  130. setAccountCreateLogs([])
  131. }
  132. }
  133. /** 存为预设 */
  134. const severBd = () => {
  135. // queryForm accountCreateLogs
  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 } = 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) {
  226. data.pageList = pageList || landingPageVOS
  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. }
  357. adLength += productList.length * targeting.length
  358. })
  359. if (mediaType === 1) {
  360. if (textType === 4) {
  361. if (adLength > newDynamicGroup.length) {
  362. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  363. return
  364. }
  365. averageAdDynamicList = splitArrayIntoRandomChunks(newDynamicGroup, adLength)
  366. } else {
  367. if (adLength > dynamicGroupLength) {
  368. message.error(`创意组分配规则选择“平均分配到广告”时,创意组总数必须大于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  369. return
  370. }
  371. averageAdDynamicList = distributeArray(newDynamicGroup, adLength)
  372. }
  373. } else if (mediaType === 2) {
  374. if (adLength < dynamicGroupLength) {
  375. message.error(`创意组分配规则选择“顺序分配到广告”时,创意组总数必须小于等于广告总数。当前创意组数量:${dynamicGroupLength},广告数量:${adLength}`)
  376. return
  377. }
  378. }
  379. }
  380. if (textType === 1) {
  381. if (dynamicGroupLength !== textDtoLenth) {
  382. message.error(`当前创意文案是“创意组一一对应”模式,创意组总数(${dynamicGroupLength})要等于创意文案总数(${textDtoLenth})`)
  383. return
  384. }
  385. if (!dynamicCreativesTextDTOS.dynamicCreativesTextDetailDTOList.every((item: {}) => item && Object.keys(item).length)) {
  386. message.error('创意文案配置错误,内容空')
  387. return
  388. }
  389. }
  390. // 落地页平均分配判断数量
  391. if ([910].includes(dynamic.creativeTemplateId) && dynamic?.landingPageType === 1) {
  392. let targetingLength = targeting.length
  393. if (accountCreateLogs.some(item => {
  394. let productListLength = item?.productList?.length || 1
  395. let total = targetingLength * productListLength
  396. let pageLength = item.pageList.length
  397. if (total > pageLength) {
  398. message.error(`当前${item.accountId}下的广告总数(${total})大于落地页总数(${pageLength}),平均分配需要落地页总数大于广告总数`)
  399. return true
  400. }
  401. return false
  402. })) {
  403. return
  404. }
  405. }
  406. let accountIndex = 0, accountIndex1 = 0
  407. accountCreateLogs.forEach(item => {
  408. let productList: any[] = []
  409. if (['MARKETING_TARGET_TYPE_FICTION', 'MARKETING_TARGET_TYPE_SHORT_DRAMA'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 小说
  410. productList = item?.productList || []
  411. } else if (['MARKETING_TARGET_TYPE_WECHAT_OFFICIAL_ACCOUNT'].includes(marketingAssetOuterSpec?.marketingTargetType)) { // 公众号
  412. productList = item?.wechatChannelList || []
  413. }
  414. let data = cartesianProduct(productList, targeting).map(newD => {
  415. let [productDto, targetDto, index] = newD
  416. let suffix = '_' + item.accountId + '_' + index
  417. let averageAdDynamic = averageAdDynamicList?.[accountIndex]
  418. let dat: any = {
  419. id: item.accountId + '_' + index,
  420. accountId: item.accountId, // 账户
  421. userActionSetsList: item.userActionSetsList, // 数据源
  422. conversionList: item.newConversionList, // 转化归因
  423. pageListDto: item.pageList, // 落地页
  424. productDto, // 商品
  425. targetDto: { // 定向
  426. ...targetDto,
  427. targetingName: targetDto.targetingName + suffix
  428. },
  429. adgroupsDto: { // 广告信息
  430. ...adgroups,
  431. adgroupName: adgroups.adgroupName + suffix
  432. },
  433. dynamicDto: dynamic, // 创意信息
  434. averageAdDynamic,
  435. rowSpan: (mediaType === 1 && textType !== 4) ? averageAdDynamic.length : ([910].includes(dynamic.creativeTemplateId) ? item.pageList?.length : (textType === 3 ? textDtoLenth * dynamicGroupLength : dynamicGroupLength)) || 1
  436. }
  437. if (marketingCarrierType === 'MARKETING_CARRIER_TYPE_WECHAT_OFFICIAL_ACCOUNT') { // 营销载体
  438. dat.marketingCarrierDto = item?.wechatChannelList
  439. }
  440. accountIndex += 1
  441. return dat
  442. })
  443. newAdCount += data.length
  444. let newData: any[] = []
  445. if ([910].includes(dynamic.creativeTemplateId)) {
  446. if (dynamic?.landingPageType === 1) {
  447. let averageAdPageList: any[] = distributeArray(item.pageList, productList.length * targeting.length)
  448. data.forEach((item, aIndex) => {
  449. let aPageList: any[] = averageAdPageList[aIndex]
  450. aPageList.forEach((page, index) => {
  451. newData.push({
  452. ...item,
  453. id: item.id + '_' + index,
  454. pageListDto: [page],
  455. dynamicDto: {
  456. ...item.dynamicDto,
  457. dynamicCreativeName: item.dynamicDto.dynamicCreativeName + index
  458. },
  459. rowSpan: index === 0 ? aPageList.length : 0,
  460. adLength: data.length,
  461. isRowSpan: true
  462. })
  463. })
  464. })
  465. } else {
  466. newData = cartesianProduct(data, item.pageList).map((item, index) => {
  467. let [d1, pageList, num] = item
  468. return {
  469. ...d1,
  470. id: d1.id + '_' + index,
  471. pageListDto: [pageList],
  472. dynamicDto: {
  473. ...d1.dynamicDto,
  474. dynamicCreativeName: d1.dynamicDto.dynamicCreativeName + num
  475. }
  476. }
  477. })
  478. }
  479. } else {
  480. if (mediaType === 1) {
  481. data.forEach(item => {
  482. const { averageAdDynamic, ...ad } = item
  483. if (textType === 3) {
  484. let rowSpan = textDtoLenth * averageAdDynamic.length
  485. cartesianProduct(textDto, averageAdDynamic).forEach((taad: any, index) => {
  486. let [textValue, aad] = taad
  487. newData.push({
  488. ...ad,
  489. id: ad.id + '_' + index,
  490. dynamicGroup: aad,
  491. textDto: textValue,
  492. rowSpan: index === 0 ? rowSpan : 0,
  493. adLength: data.length,
  494. isRowSpan: true
  495. })
  496. })
  497. } else if (textType === 4) {
  498. averageAdDynamic.forEach((aad: any, index: number) => {
  499. newData.push({
  500. ...ad,
  501. id: ad.id + '_' + index,
  502. dynamicGroup: aad,
  503. textDto: aad?.textDto,
  504. adLength: data.length,
  505. rowSpan: index === 0 ? averageAdDynamic.length : 0,
  506. isRowSpan: true
  507. })
  508. })
  509. } else {
  510. averageAdDynamic.forEach((aad: any, index: number) => {
  511. newData.push({
  512. ...ad,
  513. id: ad.id + '_' + index,
  514. dynamicGroup: aad,
  515. textDto: aad?.textDto,
  516. adLength: data.length
  517. })
  518. })
  519. }
  520. })
  521. } else if (mediaType === 2) {
  522. data.forEach((item) => {
  523. const { averageAdDynamic, ...ad } = item
  524. if (textType === 3) {
  525. cartesianProduct(textDto, [newDynamicGroup[accountIndex1 % newDynamicGroup.length]]).forEach((taad: any, i) => {
  526. let [textValue, aad, index] = taad
  527. newData.push({
  528. ...ad,
  529. id: ad.id + '_' + index,
  530. dynamicGroup: aad,
  531. textDto: textValue,
  532. rowSpan: i === 0 ? textDto.length : 0,
  533. adLength: data.length,
  534. isRowSpan: true
  535. })
  536. })
  537. } else {
  538. let { textDto, ...dynamicGroup } = newDynamicGroup[accountIndex1 % newDynamicGroup.length]
  539. newData.push({
  540. ...ad,
  541. dynamicGroup,
  542. textDto,
  543. rowSpan: 1,
  544. adLength: data.length
  545. })
  546. }
  547. accountIndex1 += 1
  548. })
  549. } else { // 全账号复用创意组 所有广告一样的创意组
  550. newData = cartesianProduct(data, newDynamicGroup.length > 0 ? newDynamicGroup : [{}]).map((item, index) => {
  551. let [d1, group] = item
  552. return {
  553. ...d1,
  554. id: d1.id + '_' + index,
  555. dynamicGroup: group,
  556. textDto: (group as any)?.textDto,
  557. adLength: data.length
  558. }
  559. })
  560. }
  561. }
  562. newdynamicCount = newdynamicCount + newData.length
  563. newTableData[item.accountId] = newData
  564. })
  565. setAdCount(newAdCount)
  566. setDynamicCount(newdynamicCount)
  567. setActiveKey(accountCreateLogs?.[0].accountId?.toString())
  568. console.log('newTableData-->', newTableData)
  569. setTableData(newTableData)
  570. }
  571. // 提交
  572. const onSubmit = (values: any) => {
  573. const { adgroups, targeting, dynamic, dynamicMaterialDTos, dynamicCreativesTextDTOS, mediaType } = JSON.parse(JSON.stringify(addelivery)) as PULLIN.AddeliveryProps
  574. let dynamicMaterialDTOS = []
  575. if (dynamic.deliveryMode === 'DELIVERY_MODE_CUSTOMIZE') {
  576. if ((materialData && Object.keys(materialData).length && dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length)) {
  577. let mType = Object.keys(materialData)[0];
  578. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: any) => {
  579. if (mType === 'image') {
  580. return [{
  581. type: mType,
  582. valueJson: JSON.stringify({
  583. value: {
  584. imageUrl: item?.image_id?.url,
  585. imageId: item?.image_id?.id,
  586. materialType: item?.image_id?.materialType
  587. }
  588. })
  589. }]
  590. } else if (mType === 'image_list' || mType === 'element_story') {
  591. let key = 'image_list'
  592. if (mType === 'element_story') {
  593. key = 'element_story'
  594. }
  595. let list = item?.[key]?.map((l: any) => {
  596. return {
  597. imageUrl: l?.url,
  598. imageId: l?.id,
  599. materialType: l?.materialType
  600. }
  601. })
  602. return [{
  603. type: mType,
  604. valueJson: JSON.stringify({
  605. value: {
  606. list
  607. }
  608. })
  609. }]
  610. } else if (['short_video', 'video'].includes(mType)) {
  611. let value: any = {
  612. materialType: item?.video_id?.materialType || item?.short_video1?.materialType || 0,
  613. videoUrl: item?.video_id?.url || item?.short_video1?.url,
  614. videoId: item?.video_id?.id || item?.short_video1?.id
  615. }
  616. if (item?.cover_id?.url) {
  617. value.imageUrl = item?.cover_id?.url
  618. value.imageId = item?.cover_id?.id
  619. value.materialCoverType = item?.cover_id?.materialType
  620. }
  621. return [{
  622. type: mType,
  623. valueJson: JSON.stringify({
  624. value
  625. })
  626. }]
  627. }
  628. return [{
  629. type: mType,
  630. valueJson: ''
  631. }]
  632. })
  633. }
  634. } else {
  635. if (dynamicMaterialDTos && Object.keys(dynamicMaterialDTos).length) {
  636. dynamicMaterialDTOS = dynamicMaterialDTos.dynamicGroup?.map((item: { list: any[] }) => {
  637. return item.list.map(l => {
  638. if (Array.isArray(l)) {
  639. return {
  640. type: 'image_list',
  641. valueJson: JSON.stringify({
  642. value: {
  643. list: l?.map((i: any) => {
  644. return {
  645. imageUrl: i?.url,
  646. imageId: i?.id,
  647. materialType: i?.materialType
  648. }
  649. })
  650. }
  651. })
  652. }
  653. } else if (l?.url?.includes('mp4')) {
  654. return {
  655. type: 'video',
  656. valueJson: JSON.stringify({
  657. value: {
  658. materialType: l?.materialType,
  659. videoUrl: l?.url,
  660. videoId: l?.id
  661. }
  662. })
  663. }
  664. } else {
  665. return {
  666. type: 'image',
  667. valueJson: JSON.stringify({
  668. value: {
  669. imageUrl: l?.url,
  670. imageId: l?.id,
  671. materialType: l?.materialType
  672. }
  673. })
  674. }
  675. }
  676. })
  677. })
  678. }
  679. }
  680. let accountIdParamDTOMap: any = {}
  681. accountCreateLogs.forEach(item => {
  682. let { pageList, productList, userActionSetsList, accountId, wechatChannelList, newConversionList, videoChannelList } = item
  683. let userActionSetsListDto = userActionSetsList?.map((item: any) => ({ id: item?.userActionSetId, type: item?.type })) // dataSourceId
  684. let map: any = {
  685. userActionSetsList: userActionSetsListDto,
  686. pageList: pageList?.map((item: { pageId: any }) => item.pageId)
  687. }
  688. if (productList) {
  689. map.productDTOS = productList?.map(item => {
  690. return { productId: item.marketingAssetId }
  691. })
  692. }
  693. if (wechatChannelList) {
  694. map.wechatChannelId = wechatChannelList?.[0]?.wechatOfficialAccountId
  695. }
  696. if (newConversionList) {
  697. map.conversionId = newConversionList?.[0]?.conversionId
  698. }
  699. if (videoChannelList) {
  700. map.videoChannelId = videoChannelList?.[0]?.wechatChannelsAccountId
  701. }
  702. accountIdParamDTOMap[accountId] = map
  703. })
  704. let dynamicCreativesDTO = { ...dynamic, mediaType }
  705. if (dynamic.deliveryMode === 'DELIVERY_MODE_COMPONENT') {
  706. dynamicCreativesDTO.creativeTemplateId = 711
  707. }
  708. delete adgroups?.isConversion // 前端控制新链路字段
  709. let params = {
  710. ...values,
  711. adgroupDTO: adgroups,
  712. targetings: targeting.map(item => ({ targetingName: item.targetingName, ...item?.targeting || {} })),
  713. dynamicCreativesDTO,
  714. dynamicCreativesTextDTOS,
  715. dynamicMaterialDTOS,
  716. accountIdParamDTOMap
  717. }
  718. // setSubVisible(false)
  719. createAdgroupTask.run(params).then(res => {
  720. if (res) {
  721. Modal.success({
  722. content: '任务提交成功',
  723. bodyStyle: { fontWeight: 700 },
  724. okText: '跳转任务列表',
  725. closable: true,
  726. onOk: () => {
  727. sessionStorage.setItem('CAMPV3', values?.taskName)
  728. window.location.href = '/#/launchSystemV3/tencentAdPutIn/taskList'
  729. },
  730. onCancel: () => {
  731. setSubVisible(false)
  732. }
  733. })
  734. }
  735. })
  736. }
  737. const clearData = () => {
  738. setTableData({})
  739. }
  740. return <Space direction="vertical" style={{ width: '100%' }}>
  741. <Spin spinning={createAdgroupTask.loading || getSelectTaskDetail.loading || getCreativeDetails.loading}>
  742. <Card
  743. size="small"
  744. title={
  745. <div>
  746. <Space>
  747. <div className={style.cardTitle}>配置区</div>
  748. </Space>
  749. </div>
  750. }
  751. className={style.createAd}
  752. >
  753. <Space wrap>
  754. <Selector label="媒体账户组">
  755. <Select
  756. mode="multiple"
  757. style={{ minWidth: 200 }}
  758. placeholder="快捷选择媒体账户组"
  759. maxTagCount={1}
  760. allowClear
  761. bordered={false}
  762. filterOption={(input: any, option: any) => {
  763. return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
  764. }}
  765. onChange={(e, option) => { getGroupAccountList(e) }}
  766. >
  767. {getGroupList?.data && getGroupList?.data?.map((item: any) => <Select.Option value={item.groupId} key={item.groupId}>{item.groupName}</Select.Option>)}
  768. </Select>
  769. </Selector>
  770. <Selector label="媒体账户">
  771. <Select
  772. mode="multiple"
  773. style={{ minWidth: 200, maxWidth: 500 }}
  774. placeholder="媒体账户(多个,,空格换行)"
  775. maxTagCount={1}
  776. allowClear
  777. bordered={false}
  778. maxTagPlaceholder={
  779. <Tooltip
  780. color="#FFF"
  781. title={<span style={{ color: '#000' }}>
  782. {accountCreateLogs?.filter((item, index) => index !== 0)
  783. ?.map((item, index) => <Tag
  784. key={index}
  785. closable
  786. onClose={() => {
  787. setAccountCreateLogs(accountCreateLogs?.filter(item1 => item1.accountId !== item.accountId))
  788. // setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
  789. // clearData()
  790. }}
  791. >{item.accountId}</Tag>)}</span>
  792. }
  793. >
  794. <span>+{accountCreateLogs?.length > 1 ? accountCreateLogs.length - 1 : 0}</span>
  795. </Tooltip>
  796. }
  797. autoClearSearchValue={false}
  798. filterOption={(input: any, option: any) => {
  799. let newInput: string[] = input ? input?.split(/[,,\n\s]+/ig).filter((item: any) => item) : []
  800. return newInput?.some(val => option!.children?.toString().toLowerCase()?.includes(val))
  801. }}
  802. value={accountCreateLogs?.map(item => item?.accountId)}
  803. onChange={(e) => {
  804. setAccountCreateLogs(e?.map((item: any) => ({ accountId: item })))
  805. }}
  806. searchValue={accSearch}
  807. onSearch={(val) => {
  808. setAccSearch(val)
  809. }}
  810. dropdownRender={menu => (
  811. <>
  812. {menu}
  813. <Divider style={{ margin: '8px 0' }} />
  814. <Space style={{ padding: '0 8px 4px' }}>
  815. <Checkbox onChange={(e) => {
  816. let data = []
  817. if (e.target.checked) {
  818. data = JSON.parse(JSON.stringify(getAllUserAccount?.data?.data))
  819. if (accSearch) {
  820. let newAccSearch = accSearch?.split(/[,,\n\s]+/ig).filter((item: any) => item)
  821. data = data?.filter((item: any) => newAccSearch?.some(val => item!.accountId?.toString().toLowerCase()?.includes(val)))
  822. }
  823. }
  824. // setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
  825. setAccountCreateLogs(data?.map((item: any) => ({ accountId: item?.accountId })))
  826. // clearData()
  827. }}>全选</Checkbox>
  828. </Space>
  829. </>
  830. )}
  831. >
  832. {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.accountId} key={item.id}>{item.remark ? item.accountId + '_' + item.remark : item.accountId}</Select.Option>)}
  833. </Select>
  834. </Selector>
  835. {accountCreateLogs?.length > 0 && <>
  836. {['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>}
  837. {(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>}
  838. {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>}
  839. {!isConversion ?
  840. <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
  841. :
  842. <Button type="primary" danger={!accountCreateLogs?.some(item => item?.newConversionList?.length)} onClick={() => { setConversionVisible(true) }}>{accountCreateLogs?.some(item => item?.newConversionList?.length) ? <>重新选择转化归因<CheckOutlined style={{ color: '#FFF' }} /></> : '请选择转化归因'}</Button>
  843. }
  844. </>}
  845. </Space>
  846. <div className={style.settingsBody}>
  847. <div className={style.settingsBody_content}>
  848. <DispatchAddelivery.Provider
  849. value={{
  850. addelivery,
  851. setAddelivery,
  852. accountCreateLogs,
  853. setAccountCreateLogs,
  854. materialData,
  855. setMaterialData,
  856. textData,
  857. setTextData,
  858. clearData
  859. }}>
  860. <div className={style.settingsBody_content_right}>
  861. {/* 广告信息 */}
  862. <Ad />
  863. {/* 定向 */}
  864. <Target />
  865. {/* 创意 */}
  866. <Dynamic
  867. creativeTemplateAppellation={creativeTemplateAppellation}
  868. creativeTemplateStyle={creativeTemplateStyle}
  869. />
  870. {/* 创意素材 */}
  871. <Material />
  872. </div>
  873. <div className={style.settingsBody_content_left}>
  874. {/* 创意文案 */}
  875. <MaterialText />
  876. {/* 落地页 */}
  877. <PageList />
  878. </div>
  879. </DispatchAddelivery.Provider>
  880. </div>
  881. </div>
  882. <Space className={style.bts} wrap>
  883. <Button type='primary' onClick={severBd}>存为预设</Button>
  884. <Popconfirm
  885. title="确定清空?"
  886. onConfirm={delBdPlan}
  887. >
  888. <Button>清空配置/预设</Button>
  889. </Popconfirm>
  890. <Button type='primary' onClick={preview}><SearchOutlined />预览广告</Button>
  891. </Space>
  892. {/* 选择小说 */}
  893. {goodsVisible && <GoodsModal
  894. marketingTargetType={marketingAssetOuterSpec?.marketingTargetType}
  895. visible={goodsVisible}
  896. data={accountCreateLogs}
  897. onClose={() => setGoodsVisible(false)}
  898. onChange={(e) => {
  899. setAccountCreateLogs(e);
  900. setGoodsVisible(false);
  901. clearData()
  902. }}
  903. />}
  904. {/* 选择数据源 */}
  905. {sourceVisible && <DataSourceModal
  906. visible={sourceVisible}
  907. data={accountCreateLogs}
  908. onClose={() => setSourceVisible(false)}
  909. onChange={(e) => {
  910. setAccountCreateLogs(e);
  911. setSourceVisible(false);
  912. clearData()
  913. }}
  914. />}
  915. {/* 选择公众号 */}
  916. {wechatVisible && <WechatAccount
  917. visible={wechatVisible}
  918. data={accountCreateLogs}
  919. onClose={() => setWechatVisible(false)}
  920. onChange={(e) => {
  921. setAccountCreateLogs(e);
  922. setWechatVisible(false);
  923. clearData()
  924. }}
  925. />}
  926. {/* 选择视频号 */}
  927. {channelsProfileVisible && <VideoChannel
  928. visible={channelsProfileVisible}
  929. data={accountCreateLogs}
  930. onClose={() => setChannelsProfileVisible(false)}
  931. onChange={(e) => {
  932. setAccountCreateLogs(e);
  933. setChannelsProfileVisible(false);
  934. clearData()
  935. }}
  936. />}
  937. {/* 转化归因 */}
  938. {conversionVisible && <ConversionSelect
  939. adgroups={addelivery.adgroups}
  940. visible={conversionVisible}
  941. data={accountCreateLogs}
  942. onClose={() => setConversionVisible(false)}
  943. onChange={(e) => {
  944. setAccountCreateLogs(e);
  945. setConversionVisible(false);
  946. clearData()
  947. }}
  948. />}
  949. </Card>
  950. </Spin>
  951. <Card
  952. className={style.createAd}
  953. >
  954. {activeKey && tableData && Object.keys(tableData)?.length > 0 ? <div className={style.cardBody}>
  955. <Tabs
  956. onChange={(e) => { setActiveKey(e) }}
  957. type="card"
  958. activeKey={activeKey}
  959. tabBarExtraContent={<Space>
  960. <span>广告总数:{adCount}</span>
  961. <span>创意总数:{dynamicCount}</span>
  962. <Button type='primary' onClick={() => {
  963. setSubVisible(true)
  964. }}>提交创建</Button>
  965. </Space>}
  966. >
  967. {accountCreateLogs.map(item => <Tabs.TabPane tab={item.accountId} key={item.accountId} />)}
  968. </Tabs>
  969. {addelivery?.dynamicCreativesTextDTOS?.type === 4 && <Title level={5} style={{ color: 'red', fontSize: 12 }}>因为选择的是“创意组和文案叉乘打乱后分配”模式,会随机打乱,当前预览广告下创意内容会与实际建出来的创意有偏差,请以建出来的为准</Title>}
  970. <div className={style.content} style={{ marginTop: 20 }}>
  971. <Table
  972. columns={columns()}
  973. dataSource={tableData[activeKey]}
  974. size="small"
  975. bordered
  976. scroll={{ x: 1200, y: 600 }}
  977. rowKey={'id'}
  978. pagination={{
  979. defaultPageSize: 50,
  980. total: tableData[activeKey]?.length || 0,
  981. showTotal: (total) => <Tag color="cyan">当前共{total}个创意,{tableData[activeKey]?.[0]?.adLength}个广告</Tag>,
  982. }}
  983. />
  984. </div>
  985. </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  986. <Empty description="请先完成模块配置后,再预览广告计划" />
  987. </div>}
  988. </Card>
  989. {/* 提交任务 */}
  990. {subVisible && <SubmitModal
  991. ajax={createAdgroupTask}
  992. visible={subVisible}
  993. onChange={(e) => {
  994. onSubmit(e)
  995. }}
  996. onClose={() => {
  997. setSubVisible(false)
  998. }}
  999. />}
  1000. </Space>
  1001. }
  1002. export default Create