index.tsx 59 KB

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