index.tsx 63 KB

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