index.tsx 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262
  1. import Tables from "@/components/Tables"
  2. import { useAjax } from "@/Hook/useAjax"
  3. import { createAdBatchApi, CreateAdProps } from "@/services/launchAdq/createAd"
  4. import { getSysAdcreativeInfo } from "@/services/launchAdq/creative"
  5. import { PromotedObjectType } from "@/services/launchAdq/enum"
  6. import { getTagsList, get_adcreative_template } from "@/services/launchAdq/global"
  7. import { getSysAdgroupsInfo } from "@/services/launchAdq/localAd"
  8. import { getsysTargetingInfo } from "@/services/launchAdq/targeting"
  9. import { CheckOutlined, CloseOutlined, SearchOutlined } from "@ant-design/icons"
  10. import { Button, Card, Col, Empty, Row, Select, Space, Spin, Tooltip, Image, message, Tabs, Popconfirm, notification } from "antd"
  11. import React, { useCallback, useEffect, useState } from "react"
  12. import { useModel } from "umi"
  13. import Ad from "./ad"
  14. import DataSourceModal from "../../components/dataSourceModal"
  15. import GoodsModal from "../../components/goodsModal"
  16. import IdModal from "../../components/idModal"
  17. import LookLanding from "../../components/lookLanding"
  18. import PageModal from "../../components/pageModal"
  19. import SelectCloud from "../../components/selectCloud"
  20. import style from './index.less'
  21. import Selector from "./selector"
  22. import SubmitModal from "./submitModal"
  23. import columns from "./tableConfig"
  24. import TargetIng from './targeting'
  25. import Creative from './creative'
  26. import AddGroup from '../../components/addGroup'
  27. import CustomerServiceModal from "../../components/customerServiceModal"
  28. import { getTaskDetailsApi } from "@/services/launchAdq/taskList"
  29. import CreativeCL from "./creativeCL"
  30. import { groupBy } from "@/utils/utils"
  31. import { getAccountListApi, getGroupListApi } from "@/services/launchAdq/subgroup"
  32. const CreateAd: React.FC = () => {
  33. /*************************/
  34. const { getAllUserAccount } = useModel('useLaunchAdq.useAdAuthorize')
  35. const [queryForm, setQueryForm] = useState<Partial<CreateAdProps>>({
  36. campaignName: '', // 计划名称
  37. campaignType: 'CAMPAIGN_TYPE_NORMAL', // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH
  38. promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT', // 推广目标类型
  39. speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
  40. sysAdgroupId: undefined, // 广告组ID
  41. sysAdgroup: undefined,//广告组内容
  42. sysTargetingId: undefined, // 定向包 id
  43. sysTargeting: undefined, // 定向包 内容
  44. adgroupName: undefined, // 广告名称
  45. configuredStatus: 'AD_STATUS_SUSPEND', // 广告状态
  46. sysAdcreativeId: undefined, // 创意ID
  47. taskMediaMaps: [], // 创意内容
  48. pageList: [],//本地落地页详情入口
  49. adqPageList: [],//云落地页
  50. expandEnabled: false,
  51. expandTargeting: [],
  52. model: 'cross'
  53. })
  54. const [launchMode, setLaunchMode] = useState<number>(Number(localStorage.getItem('LAUNCHMODE')) || 1) // 投放模式 1 现在投放模式 2 创量模式
  55. const [accountCreateLogs, setAccountCreateLogs] = useState<{ adAccountId: number, id: number, userActionSetsList?: any[], productList?: any, conversionList?: any, customAudienceList?: any, excludedCustomAudienceList?: any, pageList?: any, coldStartAudienceList?: any[] }[]>([]) // 账户
  56. const { currentUser: { userId } }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
  57. const [goodsVisible, setGoodsVisible] = useState<boolean>(false) // 选择商品弹窗控制
  58. const [sourceVisible, setSourceVisible] = useState<boolean>(false) // 选择数据源弹窗控制
  59. const [idVisible, setIdVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
  60. const [selectImgVisible, setSelectImgVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
  61. const [lookVisible, setLookVisible] = useState<boolean>(false) // 选择转化ID弹窗控制
  62. const [subVisible, setSubVisible] = useState<boolean>(false) // 选择设置名称弹窗控制
  63. const [pageVisible, setPageVisible] = useState<boolean>(false) // 选择云端落地页控制
  64. const [tableData, setTableData] = useState<any[]>([]) // 预览表格
  65. const [tableSelect, setTableSelect] = useState<any[]>([])
  66. const [geoLocationList, setGeoLocationList] = useState<any>({}) // 所有地域列表
  67. const [modelList, setModelList] = useState<any>({}) // 所有品牌手机
  68. const [targetKey, set_targetKey] = useState('0')//创意key
  69. const [page_checked, set_page_checked] = useState(false)//创意key
  70. const [usesArr, setUsersArr] = useState<any>(localStorage.getItem('ADQUSERS' + userId) ? JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any) : [])
  71. const { init, get } = useModel('useLaunchAdq.useBdMediaPup')
  72. const [cloudParams, setCloudParams] = useState<{ adcreativeTemplateId?: number }>({})
  73. const tagsList_REGION = useAjax((params) => getTagsList(params))
  74. const tagsList_MODEL = useAjax((params) => getTagsList(params))
  75. const getSysAdgroups = useAjax((params) => getSysAdgroupsInfo(params))
  76. const getsysTargeting = useAjax((params) => getsysTargetingInfo(params))
  77. const getSysAdcreative = useAjax((params) => getSysAdcreativeInfo(params))
  78. const createAdBatch = useAjax((params) => createAdBatchApi(params))
  79. const getTaskDetails = useAjax((params) => getTaskDetailsApi(params))
  80. const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
  81. const getGroupList = useAjax(() => getGroupListApi())
  82. /*************************/
  83. useEffect(() => {
  84. getGroupList.run()
  85. }, [])
  86. /** 判断出价方式,优化目标 “二方包”人群包仅能在出价方式为CPC(包含oCPM点击优化目标)、CPM场景下使用 */
  87. // useEffect(() => {
  88. // if (queryForm?.sysAdgroup && Object.keys(queryForm?.sysAdgroup).length > 0 && (!['BID_MODE_CPC', 'BID_MODE_CPM'].includes(queryForm?.sysAdgroup?.bidMode) || !(queryForm?.sysAdgroup?.bidMode === 'BID_MODE_OCPM' && queryForm?.sysAdgroup?.optimizationGoal === 'OPTIMIZATIONGOAL_CLICK'))) {
  89. // setAccountCreateLogs(() => accountCreateLogs.map(item => {
  90. // delete item?.customAudienceList
  91. // delete item?.excludedCustomAudienceList
  92. // return item
  93. // }))
  94. // }
  95. // }, [queryForm?.sysAdgroup])
  96. /**数据回填 */
  97. useEffect(() => {
  98. let taskId = sessionStorage.getItem('TASKID')
  99. if (taskId) {
  100. getTaskDetails.run(taskId).then(async res => {
  101. const { adCreateLogs, campaignType, promotedObjectType, speedMode, sysAdgroup, sysAdgroupId, sysTargeting, sysTargetingId } = res
  102. let adcreativeTemplateId = adCreateLogs[0]?.sysAdcreative?.adcreativeTemplateId
  103. let sysPageId = adCreateLogs[0]?.sysPageId
  104. let pageId = adCreateLogs[0]?.pageId
  105. let type: 1 | 2 = 1
  106. if (sysPageId && pageId) {
  107. type = 1
  108. } else if (sysPageId) {
  109. if (adCreateLogs?.every((item: { sysAdcreative: { adcreativeTemplateId: number }, sysPageId: number }) => item.sysAdcreative.adcreativeTemplateId === adcreativeTemplateId && (item.sysPageId === sysPageId))) {
  110. type = 2
  111. } else {
  112. type = 1
  113. }
  114. } else {
  115. if (adCreateLogs?.every((item: { sysAdcreative: { adcreativeTemplateId: number }, pageId: number }) => item.sysAdcreative.adcreativeTemplateId === adcreativeTemplateId && item.pageId === pageId)) {
  116. type = 2
  117. } else {
  118. type = 1
  119. }
  120. }
  121. setLaunchMode(type)
  122. /** 本地落地页处理 */
  123. let pageList: any = []
  124. /** 云端落地页 */
  125. let adqPageList: any[] = []
  126. let taskMediaMaps: any[] = []
  127. const sorted = groupBy(adCreateLogs, (item) => [item.accountId])
  128. let type2Data = {}
  129. if (type === 1) {
  130. pageList = sorted[0]?.map((item: any) => {
  131. if (item?.sysPageId) {
  132. return item.sysPage
  133. } else {
  134. return null
  135. }
  136. })
  137. adqPageList = sorted[0]?.map((item: any) => {
  138. if (item?.pageId) {
  139. return {
  140. pageList: [{ ...item.page, id: item.page.pageId }],
  141. adAccountId: item?.accountId,
  142. id: item?.adAccountId,
  143. }
  144. } else {
  145. return null
  146. }
  147. })
  148. taskMediaMaps = sorted[0]?.map((item: any, index: number) => {
  149. let pageElementsSpecList = item?.sysPage?.pageSpecsList[0]?.pageElementsSpecList // 内容区
  150. let globalSpec = item?.sysPage?.globalSpec // 悬浮组件
  151. /** 处理客服 */
  152. let cropUserGroupMap: any[] = []
  153. if ((pageElementsSpecList as any[])?.some((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX') || (globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList?.some((item: { floatButtonSpec: { elementType: string } }) => item?.floatButtonSpec?.elementType === 'ENTERPRISE_WX'))) {
  154. let groupList: { type: number, name: string, cropList: any[], cropId?: number, groupId?: number }[] = [];
  155. (pageElementsSpecList as any[])?.forEach((item: { elementType: string, enterpriseWxSpec: { btnTitle: string } }) => {
  156. if (item?.elementType === 'ENTERPRISE_WX') {
  157. groupList.push({ type: 1, name: '联系商家', cropList: [] }) // item.enterpriseWxSpec.btnTitle
  158. }
  159. })
  160. if ((globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList)) {
  161. groupList.push({ type: 2, name: '悬浮组件', cropList: [] })
  162. }
  163. cropUserGroupMap = groupBy(adCreateLogs, (item) => [item.sysAdcreativeId])[0]?.map((item: any) => {
  164. let corpUserGroup1s = item?.corpUserGroup1s
  165. let corpUserGroup2s = item?.corpUserGroup2s
  166. return {
  167. adAccountId: item.accountId, id: item.adAccountId, data: groupList.map((crop: any, index: number) => {
  168. return { ...crop, cropList: crop.type === 1 ? corpUserGroup1s[index] ? [{ ...corpUserGroup1s[index], id: corpUserGroup1s[index].groupId }] : [] : corpUserGroup2s[0] ? [{ ...corpUserGroup2s[0], id: corpUserGroup2s[0].groupId }] : [] }
  169. })
  170. }
  171. })
  172. }
  173. // 落地页信息
  174. let accountPageIdMap: any = {}
  175. adCreateLogs?.forEach((item: any) => {
  176. if (item?.pageId) {
  177. accountPageIdMap[item.accountId] = item?.pageId
  178. }
  179. })
  180. return { sysAdcreative: item?.sysAdcreative, sysPageId: item?.sysPageId, cropUserGroupMap, accountPageIdMap }
  181. })
  182. } else {
  183. let adCreateLog = adCreateLogs[0]
  184. if (adCreateLog?.sysPageId) {
  185. pageList = [adCreateLog?.sysPage]
  186. } else {
  187. pageList = [null]
  188. }
  189. if (adCreateLog?.pageId) {
  190. adqPageList = [[{
  191. pageList: [{ ...adCreateLog.page, id: adCreateLog.page.pageId }],
  192. adAccountId: adCreateLog?.accountId,
  193. id: adCreateLog?.adAccountId,
  194. }]]
  195. } else {
  196. adqPageList = [null]
  197. }
  198. let template = await getAdcreativeTemplate.run({ promotedObjectType, adcreativeTemplateId, siteSet: sysAdgroup.siteSet })
  199. let states = { kp_show: false, xd_show: true, sj_show: false, bq_show: false, sp_show: false }
  200. if (template[0]) {
  201. let pageList = template[0]?.landingPageConfig?.supportPageTypeList?.filter((i: { description: string | string[] }) => i.description.includes('微信原生推广页'))//当前版本只获取微信原生页,后期改进
  202. let pageType = pageList?.length ? pageList[0]?.pageType : null
  203. //数据展示组件
  204. if (template[0].adcreativeAttributes.some((item: { name: string }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
  205. states = { ...states, sj_show: true }
  206. }
  207. //行动按钮组件存在
  208. if (states.xd_show) {
  209. let supportLinkNameTypeData = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType
  210. let linkNameList = supportLinkNameTypeData?.list
  211. if (linkNameList) { // && !linkPageType
  212. } else {
  213. states = { ...states, xd_show: false }
  214. }
  215. if (supportLinkNameTypeData?.required) {
  216. states = { ...states, xd_show: true }
  217. }
  218. }
  219. // 视频结束页 end_page
  220. if (template[0].adcreativeElements.some((item: { name: string }) => item.name === 'end_page')) {
  221. states = { ...states, sp_show: true }
  222. }
  223. }
  224. if (adcreativeTemplateId === 2106) {
  225. template[0].adcreativeElements = template[0]?.adcreativeElements?.map((item: any) => {
  226. if (item.name === "description") {
  227. return { ...item, name: 'title', title: item['description'] }
  228. }
  229. return item
  230. })
  231. }
  232. type2Data['textData'] = template[0]?.adcreativeElements?.filter((item: any) => item.name === 'title' || (item.required && item.name === 'description')).map((item: any) => ({ ...item, pupState: states }))
  233. type2Data['materialData'] = template[0]?.adcreativeElements?.filter((item: any) => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image' || item.name === 'element_story').map((item: any) => {
  234. return {
  235. label: item.description === '图片' && res[0]?.adcreativeElements?.some((item: any) => item.name === 'video') ? '视频封面图' : item.description,
  236. name: item.name,
  237. restriction: item.restriction,
  238. arrayProperty: item?.arrayProperty
  239. }
  240. })
  241. taskMediaMaps = [sorted[0][0]].map((item: any) => {
  242. let pageElementsSpecList = item?.sysPage?.pageSpecsList[0]?.pageElementsSpecList // 内容区
  243. let globalSpec = item?.sysPage?.globalSpec // 悬浮组件
  244. /** 处理客服 */
  245. let cropUserGroupMap: any[] = []
  246. if ((pageElementsSpecList as any[])?.some((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX') || (globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList?.some((item: { floatButtonSpec: { elementType: string } }) => item?.floatButtonSpec?.elementType === 'ENTERPRISE_WX'))) {
  247. let groupList: { type: number, name: string, cropList: any[], cropId?: number, groupId?: number }[] = [];
  248. (pageElementsSpecList as any[])?.forEach((item: { elementType: string, enterpriseWxSpec: { btnTitle: string } }) => {
  249. if (item?.elementType === 'ENTERPRISE_WX') {
  250. groupList.push({ type: 1, name: '联系商家', cropList: [] }) // item.enterpriseWxSpec.btnTitle
  251. }
  252. })
  253. if ((globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList)) {
  254. groupList.push({ type: 2, name: '悬浮组件', cropList: [] })
  255. }
  256. cropUserGroupMap = groupBy(adCreateLogs, (item) => [item.sysAdcreativeId])[0]?.map((item: any) => {
  257. let corpUserGroup1s = item?.corpUserGroup1s
  258. let corpUserGroup2s = item?.corpUserGroup2s
  259. return {
  260. adAccountId: item.accountId, id: item.adAccountId, data: groupList.map((crop: any, index: number) => {
  261. return { ...crop, cropList: crop.type === 1 ? corpUserGroup1s[index] ? [{ ...corpUserGroup1s[index], id: corpUserGroup1s[index].groupId }] : [] : corpUserGroup2s[0] ? [{ ...corpUserGroup2s[0], id: corpUserGroup2s[0].groupId }] : [] }
  262. })
  263. }
  264. })
  265. }
  266. // 落地页信息
  267. let accountPageIdMap: any = {}
  268. adCreateLogs?.forEach((item: any) => {
  269. if (item?.pageId) {
  270. accountPageIdMap[item.accountId] = item?.pageId
  271. }
  272. })
  273. let { adcreativeElements, ...Adcreative } = JSON.parse(JSON.stringify(item?.sysAdcreative))
  274. delete adcreativeElements?.title
  275. delete adcreativeElements?.description
  276. delete adcreativeElements?.imageUrlList
  277. delete adcreativeElements?.elementStory
  278. delete adcreativeElements?.imageUrl
  279. delete adcreativeElements?.videoUrl
  280. delete adcreativeElements?.shortVideo1Url
  281. return { sysAdcreative: { ...Adcreative, adcreativeElements }, sysPageId: item?.sysPageId, cropUserGroupMap, accountPageIdMap }
  282. })
  283. // 处理 materials [] texts []
  284. let newMaterials: any[] = [], newTexts: any[] = []
  285. sorted[0].forEach((item: any) => {
  286. let { title, description, imageUrlList, elementStory, imageUrl, videoUrl, shortVideo1Url } = item?.sysAdcreative?.adcreativeElements
  287. let texts = {};
  288. let materials = {};
  289. if (title) texts['title'] = title;
  290. if (description) texts['description'] = description;
  291. if (imageUrlList) materials['imageUrlList'] = imageUrlList;
  292. if (elementStory) materials['elementStory'] = elementStory;
  293. if (imageUrl) materials['imageUrl'] = imageUrl;
  294. if (videoUrl) materials['videoUrl'] = videoUrl;
  295. if (shortVideo1Url) materials['shortVideo1Url'] = shortVideo1Url;
  296. newMaterials.push(materials)
  297. newTexts.push(texts)
  298. })
  299. let groupMaterials: any[] = []
  300. let groupTexts: any[] = []
  301. if (newMaterials.length > 0 && newMaterials?.every(item => Object.keys(item).length > 0)) {
  302. let firstField = Object.keys(newMaterials[0])[0]
  303. groupMaterials = groupBy(newMaterials, (item) => [item[firstField]])
  304. type2Data['materials'] = newMaterials
  305. }
  306. if (newTexts.length > 0 && newTexts?.every(item => Object.keys(item).length > 0)) {
  307. let firstField = Object.keys(newTexts[0])[0]
  308. groupTexts = groupBy(newTexts, (item) => [item[firstField]])
  309. type2Data['texts'] = newTexts
  310. }
  311. let mLength = groupMaterials.length || 1
  312. let tLength = groupTexts.length || 1
  313. if (mLength * tLength === sorted[0].length) { // 数量对上是 叉乘 否则 一对一
  314. if (groupMaterials.length > 0) type2Data['materials'] = groupMaterials.map((item: any[]) => item[0])
  315. if (groupTexts.length > 0) type2Data['texts'] = groupTexts.map((item: any[]) => item[0])
  316. type2Data['model'] = 'cross'
  317. } else {
  318. type2Data['model'] = 'corres'
  319. }
  320. }
  321. setQueryForm({
  322. ...queryForm,
  323. ...type2Data,
  324. campaignType,
  325. promotedObjectType,
  326. speedMode,
  327. sysAdgroup,
  328. sysAdgroupId,
  329. sysTargeting,
  330. sysTargetingId,
  331. adgroupName: sysAdgroup?.adgroupName,
  332. configuredStatus: sysAdgroup?.configuredStatus,
  333. expandEnabled: sysAdgroup?.expandEnabled || false,
  334. expandTargeting: sysAdgroup?.expandTargeting || [],
  335. taskMediaMaps: taskMediaMaps || [],
  336. pageList,
  337. adqPageList
  338. })
  339. // 账号信息相关
  340. let adCreateLogsData = adCreateLogs?.map((item: any) => {
  341. return {
  342. adAccountId: item?.accountId,
  343. id: item?.adAccountId,
  344. // 数据源
  345. userActionSetsList: item?.userActionSetList?.map((item: any) => ({ ...item, id: item?.userActionSetId })),
  346. // 商品
  347. productList: item?.product ? [{ ...item?.product, productCatalog: item?.productCatalog, id: Number(item?.product?.productOuterId?.replace(/\D/ig, '')) }] : undefined,
  348. coldStartAudienceList: item?.coldStartAudienceList?.map((item: any) => ({ ...item, id: item.audienceId })),
  349. // pageList: [item.page]
  350. // 定向用户群
  351. customAudienceList: item?.customAudienceList?.map((item: any) => ({ ...item, id: item.audienceId })),
  352. // 排除用户群
  353. excludedCustomAudienceList: item?.excludedCustomAudienceList?.map((item: any) => ({ ...item, id: item.audienceId }))
  354. }
  355. }).filter((item: any, index: number, self: any) => self.findIndex((i: any) => i.id == item.id) === index)
  356. setAccountCreateLogs(adCreateLogsData)
  357. })
  358. sessionStorage.removeItem('TASKID')
  359. } else {
  360. let adqAdData = localStorage.getItem('ADQAD')
  361. if (adqAdData) {
  362. const { queryForm, accountCreateLogs } = JSON.parse(adqAdData)
  363. setQueryForm({ ...queryForm })
  364. setAccountCreateLogs(accountCreateLogs)
  365. }
  366. }
  367. }, [])
  368. // 设置地域
  369. useEffect(() => {
  370. tagsList_REGION.run({ type: 'REGION' }).then(res => {
  371. if (res && Array.isArray(res)) {
  372. setGeoLocationList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
  373. prev[cur.id] = cur
  374. return prev
  375. }, {}))
  376. }
  377. })
  378. tagsList_MODEL.run({ type: 'DEVICE_BRAND_MODEL' }).then(res => {
  379. if (res && Array.isArray(res)) {
  380. setModelList(() => (res as any[])?.reduce((prev: any, cur: { id: number }) => {
  381. prev[cur.id] = cur
  382. return prev
  383. }, {}))
  384. }
  385. })
  386. }, [])
  387. // 获取账户列表
  388. useEffect(() => {
  389. getAllUserAccount.run()
  390. }, [])
  391. // 账号对比
  392. useEffect(() => {
  393. if (getAllUserAccount?.data?.data && accountCreateLogs) {
  394. if (accountCreateLogs.some(item => !getAllUserAccount?.data?.data?.find((item1: { accountId: number }) => item.adAccountId == item1.accountId))) {
  395. let errorData: any[] = []
  396. let newAccountCreateLogs = accountCreateLogs.filter(item => {
  397. let data = getAllUserAccount?.data?.data?.find((item1: { accountId: number }) => item.adAccountId == item1.accountId)
  398. if (data) {
  399. return true
  400. } else {
  401. errorData.push(item.adAccountId)
  402. return false
  403. }
  404. })
  405. notification.error({
  406. duration: 60 * 5,
  407. message: '重要提示',
  408. description: `本地媒体账户与你所拥有账户对不上,当前创建账号不符合账号及部分相关配置已清空。请把保存在本地的媒体账户或者媒体账户组清空,重新选择保存。问题账户:(${errorData.toString()})`
  409. })
  410. setAccountCreateLogs(newAccountCreateLogs)
  411. setQueryForm({ ...queryForm, adqPageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map(item => ({ ...item, accountPageIdMap: {} })) })
  412. }
  413. }
  414. }, [getAllUserAccount?.data, accountCreateLogs, queryForm])
  415. /** 获取广告详情 */
  416. useEffect(() => {
  417. if (getSysAdgroups?.data?.bidMode !== 'BID_MODE_CPM' && accountCreateLogs?.length > 0) {
  418. let newAccountCreateLogs = accountCreateLogs?.map((item: any) => {
  419. if (item?.customAudienceList) {
  420. delete item?.customAudienceList
  421. }
  422. return { ...item }
  423. })
  424. setAccountCreateLogs([...newAccountCreateLogs])
  425. }
  426. }, [getSysAdgroups?.data?.bidMode])
  427. /** 删除商品内容 */
  428. const goodsDel = (index: number) => {
  429. let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
  430. delete newArr[index].productList
  431. setAccountCreateLogs(newArr)
  432. }
  433. /** 删除数据源 */
  434. const sourceDel = (index: number, num: number) => {
  435. let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
  436. newArr[index].userActionSetsList?.splice(num, 1)
  437. setAccountCreateLogs(newArr)
  438. }
  439. /** 删除人群包 */
  440. const cpDel = (index: number, num: number, key: string) => {
  441. let newArr = JSON.parse(JSON.stringify(accountCreateLogs))
  442. newArr[index][key]?.splice(num, 1)
  443. setAccountCreateLogs(newArr)
  444. }
  445. // 创意素材与文案叉乘处理
  446. const whatever = (...arrs: any[]) => {
  447. if (arrs[0]?.length && arrs[1]?.length) {
  448. if (queryForm.model === 'corres') { // 一一对应
  449. return arrs[0].map((item: any, index: number) => ({ ...item, ...arrs[1][index] }))
  450. }
  451. return arrs.reduce((total, curr) => total.flatMap((e: any) => curr.map((e2: any) => ({ ...e2, ...e }))))
  452. } else if (arrs[0]?.length) {
  453. return arrs[0]
  454. } else if (arrs[1]?.length) {
  455. return arrs[1]
  456. } else {
  457. return ['']
  458. }
  459. }
  460. /** 预览 */
  461. const preview = () => {
  462. let newQueryForm: Partial<CreateAdProps> = JSON.parse(JSON.stringify(queryForm))
  463. if (accountCreateLogs?.length === 0) {
  464. message.error('请选择媒体账户')
  465. return
  466. }
  467. if (!newQueryForm.promotedObjectType) {
  468. message.error('请选择推广目标')
  469. return
  470. }
  471. if (!newQueryForm.sysAdgroup) {
  472. message.error('请先设置广告基本信息')
  473. return
  474. }
  475. if (!newQueryForm.sysTargeting) {
  476. message.error('请选择定向')
  477. return
  478. }
  479. if (!newQueryForm.taskMediaMaps?.every(item => item.sysAdcreative)) {
  480. message.error('请设置创意的基本信息')
  481. return
  482. }
  483. if (!newQueryForm.taskMediaMaps?.every(item => item.sysPageId || item.accountPageIdMap)) {
  484. message.error('请选择落地页')
  485. return
  486. }
  487. if (launchMode === 2) {
  488. if ((queryForm?.materialData && queryForm?.materialData?.length > 0) && !(newQueryForm?.materials && newQueryForm?.materials?.length > 0)) {
  489. message.error('请选择创意素材')
  490. return
  491. }
  492. if ((queryForm?.textData && queryForm.textData?.length > 0) && !(newQueryForm?.texts && newQueryForm?.texts?.length > 0)) {
  493. message.error('请选择创意文案')
  494. return
  495. }
  496. if (queryForm.model === 'corres' && (queryForm?.materialData && queryForm?.materialData?.length > 0) && (queryForm?.textData && queryForm.textData?.length > 0) && queryForm.texts?.length !== queryForm?.materials?.length) {
  497. message.error('素材文案一一对应模式下,素材数量与文案数量要相等,请修改')
  498. return
  499. }
  500. }
  501. if (newQueryForm?.taskMediaMaps && newQueryForm?.taskMediaMaps?.some((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)) {
  502. let cropData = newQueryForm?.taskMediaMaps?.filter((item: { cropUserGroupMap: any[] }) => item?.cropUserGroupMap?.length > 0)
  503. if (cropData?.some((item: { cropUserGroupMap: { data: { cropList: any[] }[] }[] }) => {
  504. return item?.cropUserGroupMap?.some((item1: { data: { cropList: any[] }[] }) => item1?.data?.some((item2: { cropList: any[] }) => item2?.cropList?.length === 0))
  505. })) {
  506. message.error('请完善落地页企微客服组')
  507. return
  508. }
  509. }
  510. let data: any[] = []
  511. if (launchMode === 2) {
  512. if (Array.isArray(newQueryForm.materials) && Array.isArray(newQueryForm?.texts)) {
  513. let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
  514. let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
  515. let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
  516. taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
  517. return JSON.parse(JSON.stringify(taskMediaMap))
  518. })
  519. newQueryForm.taskMediaMaps = newTaskMediaMaps
  520. }
  521. }
  522. accountCreateLogs.forEach((item: any) => {
  523. newQueryForm.taskMediaMaps?.forEach((task, index) => {
  524. let obj = {
  525. ...item,
  526. ...newQueryForm,
  527. sysAdGroupData: newQueryForm.sysAdgroup,
  528. targetingData: newQueryForm.sysTargeting,
  529. sysAdcreativeData: task.sysAdcreative,
  530. pageData: launchMode === 2 ? (newQueryForm.pageList as any)[0] || (newQueryForm.adqPageList as any)[0]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0] : (newQueryForm.pageList as any)[index] || (newQueryForm.adqPageList as any)[index]?.find((adq: { adAccountId: any }) => adq.adAccountId === item.adAccountId)?.pageList[0],
  531. myId: Number(item.id + '' + index)
  532. }
  533. data.push(obj)
  534. })
  535. })
  536. setTableData(data)
  537. }
  538. const submit = (props: { campaignName: string, count?: number }) => {
  539. let newQueryForm = JSON.parse(JSON.stringify(queryForm))
  540. if (launchMode === 2) {
  541. if (Array.isArray(newQueryForm.materials) && Array.isArray(newQueryForm?.texts)) {
  542. let taskMediaMap = JSON.parse(JSON.stringify(newQueryForm.taskMediaMaps[0]))
  543. let adcreativeElements = taskMediaMap.sysAdcreative?.adcreativeElements || {}
  544. let newTaskMediaMaps = whatever(newQueryForm.materials, newQueryForm.texts).map((item: any) => {
  545. if (item) {
  546. taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements, ...item }
  547. } else {
  548. taskMediaMap.sysAdcreative.adcreativeElements = { ...adcreativeElements }
  549. }
  550. return JSON.parse(JSON.stringify(taskMediaMap))
  551. })
  552. newQueryForm.taskMediaMaps = newTaskMediaMaps
  553. }
  554. }
  555. let newtaskMediaMaps = newQueryForm.taskMediaMaps.map((item1: { cropUserGroupMap?: any[] }) => {
  556. let { cropUserGroupMap, ...data } = item1
  557. if (cropUserGroupMap && cropUserGroupMap?.length > 0) {
  558. let corpUserGroup1Map: any = {}
  559. let corpUserGroup2Map: any = {}
  560. cropUserGroupMap.forEach((cropData: { id: number, data: any[] }) => {
  561. let cropData1: { corpId: string, groupId: number }[] = []
  562. let cropData2: { corpId: string, groupId: number }[] = []
  563. cropData?.data.forEach((crop: { type: 1 | 2, cropList: { corpId: string, groupId: number }[] }) => {
  564. let cropList = crop.cropList
  565. if (crop.type === 1) {
  566. cropData1.push({ corpId: cropList[0].corpId, groupId: cropList[0].groupId })
  567. } else {
  568. cropData2.push({ corpId: cropList[0].corpId, groupId: cropList[0].groupId })
  569. }
  570. })
  571. if (cropData1.length > 0) {
  572. corpUserGroup1Map[cropData.id.toString()] = cropData1
  573. }
  574. if (cropData2.length > 0) {
  575. corpUserGroup2Map[cropData.id.toString()] = cropData2
  576. }
  577. })
  578. return { ...data, corpUserGroup1Map: Object.keys(corpUserGroup1Map)?.length > 0 ? corpUserGroup1Map : null, corpUserGroup2Map: Object.keys(corpUserGroup2Map)?.length > 0 ? corpUserGroup2Map : null }
  579. }
  580. return data
  581. })
  582. newQueryForm.taskMediaMaps = newtaskMediaMaps
  583. let params = { ...newQueryForm, ...props }
  584. let accountLogs = accountCreateLogs.map((item: any, index) => {
  585. // userActionSetsList 数据源 productList 商品
  586. let data: any = { adAccountId: item.id, count: props.count || 1 }
  587. if (item?.userActionSetsList?.length > 0) { // 数据源
  588. data.userActionSets = item?.userActionSetsList?.map((item: any) => ({ id: item?.id, type: item?.type }))
  589. }
  590. if (item?.productList?.length > 0) { // 商品
  591. data.productId = item?.productList[0].productOuterId
  592. data.productCatalogId = item?.productList[0].productCatalogId
  593. }
  594. if (item?.customAudienceList?.length > 0) {
  595. data.customAudience = item?.customAudienceList?.map((item: any) => item.id)
  596. }
  597. if (item?.excludedCustomAudienceList?.length > 0) {
  598. data.excludedCustomAudience = item?.excludedCustomAudienceList?.map((item: any) => item.id)
  599. }
  600. if (item?.pageList) {
  601. data.pageId = item?.pageData?.id
  602. }
  603. if (item?.coldStartAudienceList?.length > 0) {
  604. data.coldStartAudience = item?.coldStartAudienceList?.map((item: any) => item.id)
  605. }
  606. return data
  607. })
  608. if (params?.expandEnabled) {
  609. params.sysAdgroup.expandEnabled = params?.expandEnabled
  610. params.sysAdgroup.expandTargeting = []
  611. }
  612. if (params?.expandTargeting?.length > 0) {
  613. params.sysAdgroup.expandTargeting = params?.expandTargeting
  614. }
  615. params.accountCreateLogs = accountLogs
  616. delete params.sysAdgroupId
  617. delete params.sysAdcreativeId
  618. delete params.sysTargetingId
  619. delete params.pageList
  620. delete params.adqPageList
  621. delete params.count
  622. delete params?.expandEnabled
  623. delete params?.expandTargeting
  624. delete params?.texts
  625. delete params?.textData
  626. delete params?.materialData
  627. delete params?.materials
  628. delete params?.model
  629. console.log('paramsSubmit====>', params)
  630. createAdBatch.run(params).then(res => {
  631. if (res) {
  632. sessionStorage.setItem('CAMP', props?.campaignName)
  633. message.success('创建成功')
  634. window.location.href = '/#/launchSystemNew/launchManage/taskList'
  635. }
  636. })
  637. }
  638. /** 清除数据 */
  639. const clearData = () => {
  640. setTableData([])
  641. setTableSelect([])
  642. }
  643. /** 存为预设 */
  644. const severBd = () => {
  645. // queryForm accountCreateLogs
  646. localStorage.setItem('ADQAD', JSON.stringify({
  647. queryForm,
  648. accountCreateLogs
  649. }))
  650. message.success('存储成功')
  651. }
  652. /** 清除 */
  653. const delBdPlan = () => {
  654. localStorage.removeItem('ADQAD')
  655. setAccountCreateLogs([])
  656. setQueryForm({
  657. campaignName: '', // 计划名称
  658. campaignType: 'CAMPAIGN_TYPE_NORMAL', // 计划类型 CAMPAIGN_TYPE_NORMAL CAMPAIGN_TYPE_SEARCH
  659. promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT', // 推广目标类型
  660. speedMode: 'SPEED_MODE_STANDARD', // 投放速度模式
  661. sysAdgroupId: undefined, // 广告组内容
  662. sysTargetingId: undefined, // 定向包 id
  663. adgroupName: undefined, // 广告名称
  664. configuredStatus: 'AD_STATUS_SUSPEND', // 广告状态
  665. sysAdcreativeId: undefined, // 创意ID
  666. expandEnabled: false,
  667. expandTargeting: [],
  668. model: 'cross'
  669. })
  670. }
  671. /** 设置落地页 */
  672. const setPage = (e: any) => {
  673. let arr: any = queryForm.taskMediaMaps || []
  674. function setUrl(item: { sysAdcreative: { overrideCanvasHeadOption: string, adcreativeElements: any } }) {
  675. if (item?.sysAdcreative?.overrideCanvasHeadOption && item?.sysAdcreative?.overrideCanvasHeadOption === "OPTION_CANVAS_OVERRIDE_CREATIVE") {
  676. let adcreativeElementsNew = { ...item.sysAdcreative.adcreativeElements }
  677. let obj = e[0].pageSpecsList[0].pageElementsSpecList[0]
  678. let { topImageSpec, topVideoSpec, topSliderSpec } = obj
  679. Object.keys(adcreativeElementsNew).forEach(key => {
  680. switch (key) {
  681. case 'imageUrl'://图素材
  682. adcreativeElementsNew[key] = topImageSpec?.imageUrl
  683. break;
  684. case 'videoUrl'://视频素材
  685. adcreativeElementsNew[key] = topVideoSpec?.videoUrl
  686. break;
  687. case 'imageUrlList'://图素材
  688. adcreativeElementsNew[key] = topSliderSpec?.imageUrlList || [topImageSpec?.imageUrl]
  689. break;
  690. case 'shortVideoStruct'://视频素材
  691. adcreativeElementsNew[key] = { shortVideo1Url: topVideoSpec?.videoUrl }
  692. break;
  693. }
  694. })
  695. return { ...item, sysPageId: e[0]?.id, accountPageIdMap: null, sysAdcreative: { ...item.sysAdcreative, adcreativeElements: adcreativeElementsNew } }
  696. }
  697. return { ...item, sysPageId: e[0]?.id, accountPageIdMap: null, }
  698. }
  699. if (page_checked) {
  700. arr = queryForm.taskMediaMaps?.map(item => {
  701. return setUrl(item)
  702. })
  703. } else {
  704. arr[targetKey as string] = setUrl(arr[targetKey as string])
  705. }
  706. getPageInfo(arr)
  707. setSelectImgVisible(false)
  708. }
  709. /** 获取落地页详情 */
  710. const getPageInfo = useCallback((arrList) => {
  711. console.log('arrList====>', arrList)
  712. if (arrList && arrList[targetKey]?.sysPageId) {
  713. get.run({ mediaType: 'PAGE', sysMediaId: arrList[targetKey]?.sysPageId }).then(res => {
  714. if (!Object.keys(res)?.includes('fail')) {
  715. let data = res
  716. let pageElementsSpecList = data?.pageSpecsList[0]?.pageElementsSpecList // 内容区
  717. let globalSpec = data?.globalSpec // 悬浮组件
  718. let arr: any = queryForm.pageList || []
  719. let adqPageArr: any = queryForm.adqPageList || []
  720. adqPageArr[targetKey] = null
  721. arr[targetKey] = data
  722. /** 处理客服 */
  723. let cropUserGroupMap: any[] = []
  724. if ((pageElementsSpecList as any[])?.some((item: { elementType: string }) => item?.elementType === 'ENTERPRISE_WX') || (globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList?.some((item: { floatButtonSpec: { elementType: string } }) => item?.floatButtonSpec?.elementType === 'ENTERPRISE_WX'))) {
  725. let groupList: { type: number, name: string, cropList: any[], cropId?: number, groupId?: number }[] = [];
  726. (pageElementsSpecList as any[])?.forEach((item: { elementType: string, enterpriseWxSpec: { btnTitle: string } }) => {
  727. if (item?.elementType === 'ENTERPRISE_WX') {
  728. groupList.push({ type: 1, name: '联系商家', cropList: [] }) // item.enterpriseWxSpec.btnTitle
  729. }
  730. })
  731. if ((globalSpec?.globalElementsSpecList?.length > 0 && globalSpec?.globalElementsSpecList)) {
  732. groupList.push({ type: 2, name: '悬浮组件', cropList: [] })
  733. }
  734. cropUserGroupMap = accountCreateLogs?.map((item: any) => ({ adAccountId: item.adAccountId, id: item.id, data: groupList }))
  735. }
  736. arrList[targetKey].cropUserGroupMap = cropUserGroupMap
  737. setQueryForm({ ...queryForm, pageList: arr, taskMediaMaps: arrList, adqPageList: adqPageArr })//设置落地页详情数组
  738. } else {
  739. //清空对应创意中的落地页ID
  740. let arr = queryForm.taskMediaMaps || []
  741. arr[targetKey].sysPageId = ''
  742. setQueryForm({ ...queryForm, taskMediaMaps: arr })
  743. }
  744. })
  745. }
  746. }, [queryForm, targetKey, accountCreateLogs])
  747. // 设置云端落地页
  748. const setAdqPage = useCallback((data) => {
  749. if (Array.isArray(data) && data.length > 0) {
  750. let objMap = {}
  751. data?.forEach(item => {
  752. objMap[item.adAccountId] = item.pageList[0].pageId
  753. })
  754. let arr: any = queryForm.taskMediaMaps || []
  755. let adqPageArr: any = queryForm.adqPageList || []
  756. let pageArr: any = queryForm.pageList || []
  757. adqPageArr[targetKey as string] = data
  758. pageArr[targetKey as string] = null
  759. delete arr[targetKey as string]?.cropUserGroupMap
  760. arr[targetKey as string] = { ...arr[targetKey as string], sysPageId: '', accountPageIdMap: objMap }
  761. // 重新设置云端数据并清空本地数据
  762. setQueryForm({ ...queryForm, taskMediaMaps: arr, adqPageList: adqPageArr, pageList: pageArr })
  763. }
  764. }, [queryForm, targetKey])
  765. // tabs新增和删除
  766. const onEdit = useCallback((targetKey: string | React.MouseEvent<Element, MouseEvent> | React.KeyboardEvent<Element>, action: 'add' | 'remove') => {
  767. if (queryForm.taskMediaMaps) {
  768. if (action === 'remove') {
  769. let arr = queryForm.taskMediaMaps
  770. let adqPageArr: any = queryForm.adqPageList || []
  771. let pageArr: any = queryForm.pageList || []
  772. adqPageArr[targetKey as string] = null
  773. pageArr[targetKey as string] = null
  774. arr[targetKey as string] = { ...arr[targetKey as string], sysPageId: '', accountPageIdMap: null }
  775. setQueryForm({ ...queryForm, taskMediaMaps: arr, pageList: pageArr, adqPageList: adqPageArr })
  776. }
  777. }
  778. }, [queryForm, targetKey])
  779. // 媒体组更新通知
  780. const usersChange = useCallback(() => {
  781. let data = JSON.parse(localStorage.getItem('ADQUSERS' + userId) as any)
  782. setUsersArr(data)
  783. }, [])
  784. // 切换投放模式
  785. const switchLaunchMode = () => {
  786. if (launchMode === 1) {
  787. setLaunchMode(2)
  788. localStorage.setItem('LAUNCHMODE', '2')
  789. } else {
  790. setLaunchMode(1)
  791. localStorage.setItem('LAUNCHMODE', '1')
  792. }
  793. delBdPlan()
  794. set_targetKey('0')
  795. }
  796. /** 获取分组里账号 */
  797. const getGroupAccountList = (ids: number[]) => {
  798. if (ids.length > 0) {
  799. let data = ids.map(id => getAccountListApi(id))
  800. Promise.all(data).then(res => {
  801. if (res?.length > 0 && res.every((item: { code: number }) => item.code === 200)) {
  802. let userArr: any[] = []
  803. res.forEach((item: { data: { adAccountList: { accountId: number, id: number }[] } }) => {
  804. item.data.adAccountList.forEach(acc => {
  805. let obj = userArr.find((item: { accountId: number }) => item.accountId === acc.accountId)
  806. if (!obj) {
  807. userArr.push(acc)
  808. }
  809. })
  810. })
  811. setAccountCreateLogs(userArr?.map((item) => ({ adAccountId: item?.accountId, id: item.id })))
  812. clearData()
  813. } else {
  814. message.error('操作异常')
  815. }
  816. })
  817. } else {
  818. setAccountCreateLogs([])
  819. }
  820. }
  821. return <Space direction="vertical" style={{ width: '100%' }}>
  822. <Card
  823. title={<Space>
  824. <div className={style.cardTitle}>配置区</div>
  825. <Popconfirm
  826. title="数据部分不会保存,是否切换?"
  827. onConfirm={switchLaunchMode}
  828. okText="是"
  829. cancelText="否"
  830. >
  831. <Button type="link" style={{ padding: 0 }}>切换投放模式</Button>
  832. </Popconfirm>
  833. </Space>}
  834. className={style.createAd}
  835. hoverable
  836. // extra={<AddGroup onChange={usersChange} pitcherData={getAdAccount?.data?.data} />}
  837. >
  838. <Space wrap>
  839. <Selector label="媒体账户组">
  840. <Select
  841. mode="multiple"
  842. style={{ minWidth: 200 }}
  843. placeholder="快捷选择媒体账户组"
  844. maxTagCount={1}
  845. allowClear
  846. bordered={false}
  847. filterOption={(input: any, option: any) => {
  848. return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
  849. }}
  850. onChange={(e, option) => { getGroupAccountList(e) }}
  851. >
  852. {getGroupList.data?.map((item: any) => <Select.Option value={item.groupId} key={item.groupId}>{item.groupName}</Select.Option>)}
  853. </Select>
  854. </Selector>
  855. <Selector label="媒体账户">
  856. <Select
  857. mode="multiple"
  858. style={{ minWidth: 200 }}
  859. placeholder="请选择媒体账户"
  860. maxTagCount={1}
  861. allowClear
  862. bordered={false}
  863. dropdownMatchSelectWidth={false}
  864. filterOption={(input: any, option: any) => {
  865. return option!.children?.toString().toLowerCase().includes(input.toLowerCase())
  866. }}
  867. value={accountCreateLogs?.map((item: { id: number }) => item?.id)}
  868. onChange={(e, option) => {
  869. console.log(option)
  870. setQueryForm({ ...queryForm, adqPageList: [], pageList: [], taskMediaMaps: queryForm?.taskMediaMaps?.map((item: { sysPageId: number }) => ({ ...item, sysPageId: '', accountPageIdMap: {}, cropUserGroupMap: [] })) })
  871. setAccountCreateLogs(option?.map((item: any) => ({ adAccountId: item?.children?.toString()?.split('——')[0], id: item?.value })))
  872. clearData()
  873. }}
  874. >
  875. {getAllUserAccount?.data?.data?.map((item: any) => <Select.Option value={item.id} key={item.id}>{item.remark ? item.accountId + '——' + item.remark : item.accountId}</Select.Option>)}
  876. </Select>
  877. </Selector>
  878. <Selector label="推广目标">
  879. <Select style={{ width: 200 }} value={queryForm?.promotedObjectType} placeholder="请选择推广目标" bordered={false} showSearch filterOption={(input: any, option: any) =>
  880. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  881. } onChange={(e) => { setQueryForm({ ...queryForm, promotedObjectType: e, sysAdgroup: null, sysAdgroupId: undefined, taskMediaMaps: [], sysAdcreativeId: undefined, materials: [], textData: [], texts: [] }); clearData() }}>
  882. {Object.keys(PromotedObjectType).map(key => {
  883. return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
  884. })}
  885. </Select>
  886. </Selector>
  887. {launchMode === 2 && accountCreateLogs?.length > 0 && <>
  888. <Button onClick={() => { setGoodsVisible(true) }}>商品广告(选填){accountCreateLogs?.some(item => item?.productList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
  889. <Button onClick={() => { setSourceVisible(true) }}>精准匹配归因(选填){accountCreateLogs?.some(item => item?.userActionSetsList?.length) && <CheckOutlined style={{ color: '#1890ff' }} />}</Button>
  890. </>}
  891. </Space>
  892. <div className={style.cardBody}>
  893. <Row className={style.content}>
  894. <Col span={launchMode === 1 ? 12 : 8} xl={launchMode === 1 ? 12 : 8} lg={24} md={24} sm={24} xs={24} className={style.conLeft}>
  895. <Row className={`${style.conTitle} ${style.conRightBorder}`}><Col span={24}>广告</Col></Row>
  896. <Row className={style.items}>
  897. {/* =============广告基本信息=========== */}
  898. <Ad queryForm={queryForm} setQueryForm={setQueryForm} getSysAdgroups={getSysAdgroups} clearData={clearData} />
  899. {/* =============定向包=========== */}
  900. <TargetIng
  901. queryForm={queryForm}
  902. setQueryForm={setQueryForm}
  903. getSysAdgroups={getSysAdgroups}
  904. clearData={clearData}
  905. setAccountCreateLogs={setAccountCreateLogs}
  906. getsysTargeting={getsysTargeting}
  907. geoLocationList={geoLocationList}
  908. modelList={modelList}
  909. cpDel={cpDel}
  910. accountCreateLogs={accountCreateLogs}
  911. />
  912. {launchMode === 1 && <>
  913. {/* =============商品=========== */}
  914. <Col className={style.conRightBorder} span={5}>
  915. <div className={style.top}>
  916. 商品
  917. </div>
  918. <div className={style.center}>
  919. <div className={style.centerContent}>
  920. {accountCreateLogs?.map((item: any, index: number) => {
  921. if (item?.productList) {
  922. return <div className={style.acc} key={index}>
  923. <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
  924. {
  925. item?.productList?.map((pack: { productName: string, author: string, id: number }, index: number) => {
  926. return <div className={style.accCon} key={pack.id}>{pack.productName}<CloseOutlined className={style.close} onClick={() => {
  927. goodsDel(index)
  928. }} /></div>
  929. })
  930. }
  931. </div>
  932. } else {
  933. return null
  934. }
  935. })}
  936. </div>
  937. </div>
  938. <div className={style.bottom}>
  939. {accountCreateLogs?.length > 0 ? <span onClick={() => { setGoodsVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
  940. <span>编辑</span>
  941. </Tooltip>}
  942. </div>
  943. </Col>
  944. {/* 数据源 */}
  945. <Col className={style.conRightBorder} span={5}>
  946. <div className={style.top}>
  947. 数据源
  948. </div>
  949. <div className={style.center}>
  950. <div className={style.centerContent}>
  951. {accountCreateLogs?.map((item: any, index: number) => {
  952. if (item?.userActionSetsList && item?.userActionSetsList?.length > 0) {
  953. return <div className={style.acc} key={index}>
  954. <div className={style.accName} style={{ fontWeight: 800 }}>{item.adAccountId}</div>
  955. {
  956. item?.userActionSetsList?.map((pack: { name: string, type: string, id: number }, index1: number) => {
  957. return <div className={style.accCon} key={pack.id}> <span className={style.title}>{pack.name}{' > '}{pack.type?.replace('USER_ACTION_SET_TYPE_', '')}</span> <CloseOutlined className={style.close} onClick={() => {
  958. sourceDel(index, index1)
  959. }} /></div>
  960. })
  961. }
  962. </div>
  963. } else {
  964. return null
  965. }
  966. })}
  967. </div>
  968. </div>
  969. <div className={style.bottom}>
  970. {accountCreateLogs?.length > 0 ? <span onClick={() => { setSourceVisible(true) }}>编辑</span> : <Tooltip title="请先选择媒体账户">
  971. <span>编辑</span>
  972. </Tooltip>}
  973. </div>
  974. </Col>
  975. </>}
  976. </Row>
  977. </Col>
  978. {/* =============广告创意=========== */}
  979. {launchMode === 1 ? <Col span={12} xl={12} lg={24} md={24} sm={24} xs={24} className={style.conRight}>
  980. <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
  981. <Row className={style.items}>
  982. {/* 创意 */}
  983. <Creative queryForm={queryForm} setQueryForm={setQueryForm} getSysAdgroups={getSysAdgroups} clearData={clearData} getSysAdcreative={getSysAdcreative} set_targetKey={set_targetKey} targetKey={targetKey} page_checked={page_checked} />
  984. {/* 落地页 */}
  985. <Col span={12} >
  986. <div className={style.top}>
  987. 落地页
  988. {(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap?.length > 0) && <CustomerServiceModal data={queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap} onChange={(data) => {
  989. let newQueryForm = JSON.parse(JSON.stringify(queryForm))
  990. newQueryForm.taskMediaMaps[targetKey].cropUserGroupMap = data
  991. setQueryForm(newQueryForm)
  992. }} />}
  993. </div>
  994. <div className={style.center}>
  995. <Tabs size={'small'} onEdit={onEdit} type="editable-card" activeKey={targetKey} onChange={(key) => { set_targetKey(key) }} hideAdd >
  996. {
  997. queryForm?.taskMediaMaps?.map((item, index) => {
  998. return <Tabs.TabPane tab={'创意' + (index + 1)} key={index} >
  999. <Spin spinning={get.loading}>
  1000. <div className={style.centerContent}>
  1001. {
  1002. item?.sysPageId || item?.accountPageIdMap ? <>
  1003. {
  1004. (item?.sysPageId && queryForm?.pageList) && <>
  1005. <div>落地页名称:{queryForm?.pageList[targetKey]?.pageName || ''}</div>
  1006. <div>分享名称:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareTitle || ''}</div>
  1007. <div>分享描述:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareDescription || ''}</div>
  1008. <div style={{ marginBottom: 10 }}>原生推广页顶部素材预览:
  1009. <div>{queryForm?.pageList[targetKey]?.pageSpecsList && queryForm?.pageList[targetKey]?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
  1010. switch (item?.elementType) {
  1011. case 'TOP_IMAGE':
  1012. return <Image width={80} src={item?.topImageSpec?.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
  1013. case 'TOP_SLIDER':
  1014. return <Space wrap key={index}>
  1015. {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
  1016. </Space>
  1017. case 'TOP_VIDEO':
  1018. return <video src={item?.topVideoSpec?.videoUrl} width={150} controls key={index}></video>
  1019. }
  1020. })}</div>
  1021. </div>
  1022. </>
  1023. }
  1024. {
  1025. queryForm?.adqPageList && queryForm?.adqPageList[targetKey]?.map((adq: any) => {
  1026. return <div className={style.acc} key={adq.adAccountId}>
  1027. <div className={style.accName} style={{ fontWeight: 800 }}>{adq.adAccountId}</div>
  1028. <div className={style.accCon}>
  1029. <span className={style.title}>{adq.pageList[0].pageName}</span>
  1030. </div>
  1031. </div>
  1032. })
  1033. }
  1034. </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
  1035. </div>
  1036. </Spin>
  1037. </Tabs.TabPane>
  1038. })
  1039. }
  1040. </Tabs>
  1041. </div>
  1042. <div className={style.bottom}>{
  1043. (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative) ? <>
  1044. {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
  1045. <Button type="link" onClick={() => {
  1046. setSelectImgVisible(true)
  1047. // 判定是否用原生页顶部替换外部素材
  1048. if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  1049. init({ mediaType: 'PAGE', cloudSize: undefined, adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
  1050. } else {
  1051. init({ mediaType: 'PAGE', cloudSize: undefined })
  1052. }
  1053. }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
  1054. {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
  1055. setPageVisible(true)
  1056. if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  1057. setCloudParams({ adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
  1058. } else {
  1059. setCloudParams({})
  1060. }
  1061. }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
  1062. <Button type="link">云端落地页</Button>
  1063. </Tooltip>}
  1064. </> : <Tooltip title="请先设置创意">
  1065. <Button type="link"><span>选择落地页</span></Button>
  1066. </Tooltip>}
  1067. </div>
  1068. </Col>
  1069. </Row>
  1070. </Col> : <Col span={16} xl={16} lg={24} md={24} sm={24} xs={24} className={style.conRight}>
  1071. <Row className={style.conTitle}><Col span={24}>广告创意</Col></Row>
  1072. <Row className={style.items}>
  1073. {/* 创意 */}
  1074. <CreativeCL queryForm={queryForm} setQueryForm={setQueryForm} clearData={clearData} getSysAdcreative={getSysAdcreative} targetKey={targetKey} />
  1075. {/* 落地页 */}
  1076. <Col className={style.conRightBorder} style={{ maxWidth: '25%', border: 'none' }}>
  1077. <div className={style.top}>
  1078. 落地页
  1079. {(queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap?.length > 0) && <CustomerServiceModal data={queryForm?.taskMediaMaps[targetKey]?.cropUserGroupMap} onChange={(data) => {
  1080. let newQueryForm = JSON.parse(JSON.stringify(queryForm))
  1081. newQueryForm.taskMediaMaps[targetKey].cropUserGroupMap = data
  1082. setQueryForm(newQueryForm)
  1083. }} />}
  1084. </div>
  1085. <div className={style.center}>
  1086. {queryForm?.taskMediaMaps?.filter((item, index) => index === 0)?.map((item, index) => {
  1087. return <Spin spinning={get.loading} key={index}>
  1088. <div className={style.centerContent}>
  1089. {item?.sysPageId || item?.accountPageIdMap ? <>
  1090. {(item?.sysPageId && queryForm?.pageList) && <>
  1091. <div>落地页名称:{queryForm?.pageList[targetKey]?.pageName || ''}</div>
  1092. <div>分享名称:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareTitle || ''}</div>
  1093. <div>分享描述:{queryForm?.pageList[targetKey]?.shareContentSpec?.shareDescription || ''}</div>
  1094. <div style={{ marginBottom: 10 }}>原生推广页顶部素材预览:
  1095. <div>{queryForm?.pageList[targetKey]?.pageSpecsList && queryForm?.pageList[targetKey]?.pageSpecsList[0]?.pageElementsSpecList?.filter((item: any, index: number) => index === 0)?.map((item: { elementType: 'TOP_IMAGE' | 'TOP_VIDEO' | 'TOP_SLIDER', topImageSpec: any, topSliderSpec: any, topVideoSpec: any }, index: number) => {
  1096. switch (item?.elementType) {
  1097. case 'TOP_IMAGE':
  1098. return <Image width={80} src={item?.topImageSpec?.imageUrl} style={{ borderRadius: 8, overflow: 'hidden' }} key={index} />
  1099. case 'TOP_SLIDER':
  1100. return <Space wrap key={index}>
  1101. {item?.topSliderSpec?.imageUrlList?.map((url: string, index: number) => <Image width={70} src={url} style={{ borderRadius: 8 }} key={'TOP_SLIDER' + index} />)}
  1102. </Space>
  1103. case 'TOP_VIDEO':
  1104. return <video src={item?.topVideoSpec?.videoUrl} width={150} controls key={index}></video>
  1105. }
  1106. })}</div>
  1107. </div>
  1108. </>}
  1109. {queryForm?.adqPageList && queryForm?.adqPageList[targetKey]?.map((adq: any) => {
  1110. return <div className={style.acc} key={adq.adAccountId}>
  1111. <div className={style.accName} style={{ fontWeight: 800 }}>{adq.adAccountId}</div>
  1112. <div className={style.accCon}>
  1113. <span className={style.title}>{adq.pageList[0].pageName}</span>
  1114. </div>
  1115. </div>
  1116. })}
  1117. </> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
  1118. </div>
  1119. </Spin>
  1120. })}
  1121. </div>
  1122. <div className={style.bottom}>{
  1123. (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative) ? <>
  1124. {queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId && <Button type="link" onClick={() => { setLookVisible(true) }}>查看</Button>}
  1125. <Button type="link" onClick={() => {
  1126. setSelectImgVisible(true)
  1127. // 判定是否用原生页顶部替换外部素材
  1128. if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  1129. init({ mediaType: 'PAGE', cloudSize: undefined, adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
  1130. } else {
  1131. init({ mediaType: 'PAGE', cloudSize: undefined })
  1132. }
  1133. }}>{queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysPageId ? '修改' : '选择落地页'}</Button>
  1134. {accountCreateLogs?.length > 0 ? <Button type="link" onClick={() => {
  1135. setPageVisible(true)
  1136. if (queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  1137. setCloudParams({ adcreativeTemplateId: queryForm?.taskMediaMaps[targetKey]?.sysAdcreative?.adcreativeTemplateId })
  1138. } else {
  1139. setCloudParams({})
  1140. }
  1141. }}>云端落地页</Button> : <Tooltip title="请先选择媒体账户">
  1142. <Button type="link">云端落地页</Button>
  1143. </Tooltip>}
  1144. </> : <Tooltip title="请先设置创意">
  1145. <Button type="link"><span>选择落地页</span></Button>
  1146. </Tooltip>}
  1147. </div>
  1148. </Col>
  1149. </Row>
  1150. </Col>}
  1151. </Row>
  1152. {/* =============广告底部按钮=========== */}
  1153. <Space className={style.bts}>
  1154. <Button type='primary' onClick={severBd}>存为预设</Button>
  1155. <Button type='primary' onClick={preview}><SearchOutlined /> 批量预览广告</Button>
  1156. <Button onClick={delBdPlan}>清空配置/预设</Button>
  1157. </Space>
  1158. </div>
  1159. </Card>
  1160. <Card
  1161. className={style.createAd}
  1162. hoverable
  1163. extra={tableData?.length > 0 ? <Space>
  1164. <span>推广计划总数:{tableData?.length}</span>
  1165. <span>广告总数:{tableData?.length}</span>
  1166. {/* {tableSelect?.length > 0 && <span> 已选:<span style={{ color: '#1890FF' }}>{tableSelect?.length}</span> 条</span>} */}
  1167. {
  1168. <Button type='primary' onClick={() => {
  1169. // if (tableSelect.length === 0) {
  1170. // message.error('请选择要提交的计划!')
  1171. // return
  1172. // };
  1173. setSubVisible(true)
  1174. }}>批量提交审核</Button>
  1175. }
  1176. </Space> : false
  1177. }
  1178. >
  1179. {tableData?.length > 0 ? <div className={style.cardBody}>
  1180. <div className={style.content} style={{ marginTop: 20 }}>
  1181. <Tables
  1182. columns={columns()}
  1183. dataSource={tableData}
  1184. total={0}
  1185. size="small"
  1186. bordered
  1187. scroll={{ x: 2000 }}
  1188. myKey={'myId'}
  1189. // rowSelection={{
  1190. // selectedRowKeys: tableSelect?.map((item: any) => item?.myId.toString()),
  1191. // onChange: (selectedRowKeys: React.Key[], selectedRows: any) => {
  1192. // setTableSelect(selectedRows)
  1193. // }
  1194. // }}
  1195. />
  1196. </div>
  1197. </div> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  1198. <Empty description="请先完成模块配置后,再预览广告计划" />
  1199. </div>}
  1200. </Card>
  1201. {/* 选择商品 */}
  1202. {goodsVisible && <GoodsModal visible={goodsVisible} data={accountCreateLogs} onClose={() => setGoodsVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setGoodsVisible(false); clearData() }} />}
  1203. {/* 选择数据源 */}
  1204. {sourceVisible && <DataSourceModal visible={sourceVisible} promotedObjectType={queryForm.promotedObjectType} data={accountCreateLogs} onClose={() => setSourceVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
  1205. {/* 选择转化ID */}
  1206. {idVisible && <IdModal visible={idVisible} data={accountCreateLogs} onClose={() => setIdVisible(false)} onChange={(e) => { setAccountCreateLogs(e); setSourceVisible(false); clearData() }} />}
  1207. {/* 选择ADQ落地页 */}
  1208. {pageVisible && <PageModal cloudParams={cloudParams} visible={pageVisible} data={queryForm?.adqPageList?.[targetKey] || accountCreateLogs} onClose={() => setPageVisible(false)} onChange={(e) => { setAdqPage(e); setPageVisible(false); clearData() }} />}
  1209. {/* 选择素材 */}
  1210. {selectImgVisible && <SelectCloud visible={selectImgVisible} onClose={() => setSelectImgVisible(false)} onChange={setPage} isBack={false} />}
  1211. {/* 查看落地页 */}
  1212. {lookVisible && <LookLanding visible={lookVisible} onClose={() => setLookVisible(false)} id={queryForm?.taskMediaMaps && queryForm?.taskMediaMaps[targetKey].sysPageId} />}
  1213. {/* 设置名称 */}
  1214. {subVisible && <SubmitModal data={getSysAdgroups?.data} visible={subVisible} onClose={() => setSubVisible(false)} onChange={submit} ajax={createAdBatch} />}
  1215. </Space>
  1216. }
  1217. export default CreateAd