index.tsx 69 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145
  1. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  2. import { Modal, Form, Input, Divider, Select, Radio, Switch, Spin, List, Checkbox, Space, Button, message, Image, Empty } from 'antd'
  3. import styles from './index.less'
  4. import { useAjax } from '@/Hook/useAjax'
  5. import { getText, get_adcreative_template, get_adcreative_template_list, get_tools_video_capture } from '@/services/launchAdq/global'
  6. import { AdcreativeTemplate, AdcreativeTemplateList } from '@/services/launchAdq'
  7. import { mySet } from '@/utils/arrFn'
  8. import SelectCloud from '@/pages/launchSystemNew/components/selectCloud'
  9. import { useModel } from 'umi'
  10. import { ModalConfig } from '../../ad';
  11. import { outAdcreativeTemplateIdFun } from '../../../localAd/adenum'
  12. import { CreateAdProps } from '@/services/launchAdq/createAd'
  13. import { createSysAdcreative } from '@/services/launchAdq/creative'
  14. import { creativeConfig, overrideCanvasHeadOptionEnum } from './config'
  15. import BrandImage from './brandImage'
  16. import HeadNickJump from './headNickJump'
  17. import moment from 'moment'
  18. interface Props {
  19. queryForm: Partial<CreateAdProps>,
  20. title?: string,
  21. visible: boolean,
  22. PupFn: (arg: ModalConfig) => void,
  23. callback: (params: any) => void,
  24. confirmLoading?: boolean,
  25. type?: 'add' | 'look' | 'edit',//新增,查看,编辑
  26. dataInfo?: any
  27. }
  28. /**创意模板*/
  29. function CreativePup(props: Props) {
  30. let { visible, confirmLoading, PupFn, callback, type, dataInfo, queryForm } = props
  31. const { currentUser }: any = useModel('@@initialState', model => ({ currentUser: model.initialState?.currentUser }))
  32. let [template_checked, settemplate_checked] = useState<boolean>(dataInfo?.isTemplate || false)
  33. let { promotedObjectType, sysAdgroup } = queryForm
  34. let { siteSet } = sysAdgroup
  35. const { init } = useModel('useLaunchAdq.useBdMediaPup')
  36. let arg = type === 'look' ? { footer: null } : {}
  37. // 请求
  38. const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
  39. const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
  40. const getTextLsit = useAjax((params) => getText(params))
  41. const addSysAdgroup = useAjax((params) => createSysAdcreative(params))
  42. const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
  43. // 变量
  44. const [adcreative_template, set_adcreative_template] = useState<AdcreativeTemplate>()
  45. const [adcreative_template_list, set_adcreative_template_list] = useState<AdcreativeTemplateList[]>([])
  46. const [selectImgVisible, set_selectImgVisible] = useState(false)
  47. const [selectVideoVisible, set_selectVideoVisible] = useState(false)
  48. const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
  49. const [descriptionShow, setdescriptionshow] = useState(false)
  50. const [endPageDescShow, setendPageDescnshow] = useState(false)
  51. const [isShowSc, set_isShowSc] = useState(false)//是否展示素材选项
  52. const [infoSet, set_infoSet] = useState(false)//回填设置已完成
  53. const [isShowXd, setIsShowXd] = useState(false)
  54. const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
  55. activeUrl: string,//选中的视频封面图地址
  56. preview: boolean,//是否开启图片点击预览
  57. urlList: any[],//生成的视频封面列表
  58. }>({
  59. activeUrl: '',
  60. preview: false,
  61. urlList: [
  62. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
  63. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
  64. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
  65. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
  66. ]
  67. })
  68. const [titleShow, settitleshow] = useState(false)
  69. const [form] = Form.useForm();
  70. const [pupState, setPupState] = useState({
  71. kp_show: false,
  72. xd_show: false,
  73. sj_show: false,
  74. bq_show: false,
  75. sp_show: false
  76. })
  77. const [imgMaterialConfig, setImgMaterialConfig] = useState<{
  78. adcreativeTemplateId?: number,
  79. type: string,
  80. cloudSize: { relation: string, width: number, height: number }[],
  81. list: any[],
  82. max: number
  83. }>({
  84. type: '',//类型
  85. cloudSize: [],//素材搜索条件
  86. list: [],//素材
  87. max: 1,//素材数量
  88. })//图片素材配置
  89. const [imgListMaterialConfig, setImgListMaterialConfig] = useState<{
  90. adcreativeTemplateId?: number,
  91. type: string,
  92. cloudSize: { relation: string, width: number, height: number }[],
  93. list: any[],
  94. max: number
  95. }>({
  96. type: '',//类型
  97. cloudSize: [],//素材搜索条件
  98. list: [],//素材
  99. max: 1,//素材数量
  100. })//图片素材配置
  101. const [videoMaterialConfig, setVideoMaterialConfig] = useState<{
  102. adcreativeTemplateId?: number,
  103. type: string,
  104. cloudSize: { relation: string, width: number, height: number }[],
  105. list: any[],
  106. max: number
  107. }>({
  108. type: '',//类型
  109. cloudSize: [],//素材搜索条件
  110. list: [],//素材
  111. max: 1,//素材数量
  112. })//图片素材配置
  113. const [imgType, setimgType] = useState<'single' | 'many'>('single')
  114. const [conversionList, setConversionList] = useState<any>(null)
  115. let pageType = Form.useWatch('pageType', form)
  116. let adcreativeTemplateId = Form.useWatch('adcreativeTemplateId', form)
  117. let actionBtn = Form.useWatch('actionBtn', form)
  118. // let siteSet = Form.useWatch('siteSet', form)
  119. let overrideCanvasHeadOption = Form.useWatch('overrideCanvasHeadOption', form)
  120. let adcreativeElementsType = Form.useWatch('adcreativeElementsType', form)
  121. let dataShow = Form.useWatch('dataShow', form)
  122. let conversionDataType = Form.useWatch('conversionDataType', form)
  123. let titles = Form.useWatch('title', form)
  124. let description = Form.useWatch('description', form)
  125. let videoOver = Form.useWatch('videoOver', form)
  126. let endPageDesc = Form.useWatch('endPageDesc', form)
  127. let linkPageType = Form.useWatch('linkPageType', form)
  128. // 确定事件
  129. const handleOk = useCallback(() => {
  130. form.validateFields().then(values => {
  131. console.log('values=>1', values)
  132. let newValues = JSON.parse(JSON.stringify(values))
  133. for (let key in newValues) {
  134. switch (key) {
  135. case 'image'://图素材
  136. newValues.adcreativeElements = {
  137. ...newValues.adcreativeElements,
  138. imageUrl: imgMaterialConfig?.list[0]?.url,
  139. }
  140. delete newValues[key]
  141. break;
  142. case 'video'://视频素材
  143. newValues.adcreativeElements = {
  144. ...newValues.adcreativeElements,
  145. videoUrl: videoMaterialConfig?.list[0]?.url,
  146. }
  147. delete newValues[key]
  148. break;
  149. case 'image_list'://图素材
  150. newValues.adcreativeElements = {
  151. ...newValues.adcreativeElements,
  152. imageUrlList: imgListMaterialConfig.list?.map(item => item.url),
  153. description: newValues.description,
  154. }
  155. delete newValues[key]
  156. break;
  157. case 'element_story'://图素材
  158. newValues.adcreativeElements = {
  159. ...newValues.adcreativeElements,
  160. elementStory: imgMaterialConfig.list?.map(item => ({ imageUrl: item.url })),
  161. description: newValues.description,
  162. }
  163. delete newValues[key]
  164. break;
  165. case 'short_video1'://视频素材
  166. newValues.adcreativeElements = {
  167. ...newValues.adcreativeElements,
  168. shortVideoStruct: {
  169. shortVideo1Url: videoMaterialConfig?.list[0]?.url
  170. },
  171. description: newValues.description,
  172. }
  173. delete newValues[key]
  174. break;
  175. case 'description'://文案
  176. newValues.adcreativeElements = { ...newValues.adcreativeElements, description: newValues.description }
  177. break;
  178. case 'title'://文案
  179. newValues.adcreativeElements = { ...newValues.adcreativeElements, title: newValues.title }
  180. break;
  181. case 'endPageType'://视频结束l类型
  182. newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageType: newValues.endPageType } }
  183. delete newValues[key]
  184. break;
  185. case 'endPageDesc'://视频结束文案
  186. newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageDesc: newValues.endPageDesc } }
  187. delete newValues[key]
  188. break;
  189. case 'buttonText'://特殊行动按钮
  190. newValues.adcreativeElements = { ...newValues.adcreativeElements, buttonText: newValues.buttonText }
  191. delete newValues[key]
  192. break;
  193. case 'brand'://品牌形象
  194. newValues.adcreativeElements = {
  195. ...newValues.adcreativeElements, brand: {
  196. brandName: newValues.brand.split('_')[0],
  197. brandImgUrl: newValues.brand.split('_')[1]
  198. }
  199. }
  200. break;
  201. case 'profile':
  202. newValues.adcreativeElements = {
  203. ...newValues.adcreativeElements, brand: {
  204. brandName: newValues.profile.split('_')[0],
  205. brandImgUrl: newValues.profile.split('_')[1]
  206. }
  207. }
  208. newValues.profile = {
  209. headImageUrl: newValues.profile.split('_')[1],
  210. profileName: newValues.profile.split('_')[0],
  211. description: newValues.profile.split('_')[2]
  212. }
  213. break
  214. case 'pageUrl'://跳转落地页
  215. newValues.linkPageSpec = {
  216. ...newValues.linkPageSpec,
  217. pageUrl: newValues.pageUrl
  218. }
  219. delete newValues.pageUrl
  220. break;
  221. case 'miniProgramId':
  222. newValues.linkPageSpec = {
  223. ...newValues.linkPageSpec,
  224. miniProgramSpec: {
  225. miniProgramId: newValues.miniProgramId,
  226. miniProgramPath: newValues.miniProgramPath
  227. }
  228. }
  229. delete newValues.miniProgramId
  230. delete newValues.miniProgramPath
  231. break;
  232. }
  233. }
  234. if (!newValues.adcreativeElements) {
  235. newValues.adcreativeElements = {}
  236. }
  237. //假如不存在promotedObjectType
  238. if (!newValues?.promotedObjectType) {
  239. newValues['promotedObjectType'] = queryForm.promotedObjectType
  240. }
  241. // 假如不存在siteSet
  242. if (!newValues?.siteSet) {
  243. newValues['siteSet'] = queryForm.sysAdgroup.siteSet
  244. }
  245. delete newValues.description //删除外层文案
  246. delete newValues.title //删除外层文案
  247. delete newValues.adcreativeElementsType //删除创意形式
  248. delete newValues.dataShow //删除数据开关
  249. delete newValues.actionBtn //删除行动开关
  250. delete newValues.brand //品牌形象
  251. // 假如使用了落地页顶部素材替换外部素材
  252. if (newValues.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  253. console.log(adcreative_template?.adcreativeElements)
  254. adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').forEach(item => {
  255. switch (item.name) {
  256. case 'image'://图素材
  257. newValues.adcreativeElements = {
  258. ...newValues.adcreativeElements,
  259. imageUrl: '',
  260. }
  261. break;
  262. case 'video'://视频素材
  263. newValues.adcreativeElements = {
  264. ...newValues.adcreativeElements,
  265. videoUrl: '',
  266. }
  267. break;
  268. case 'image_list'://图素材
  269. newValues.adcreativeElements = {
  270. ...newValues.adcreativeElements,
  271. imageUrlList: [],
  272. }
  273. break;
  274. case 'short_video1'://视频素材
  275. newValues.adcreativeElements = {
  276. ...newValues.adcreativeElements,
  277. shortVideoStruct: {
  278. shortVideo1Url: ''
  279. },
  280. }
  281. break;
  282. }
  283. })
  284. }
  285. console.log('newValues=>2', newValues)
  286. newValues['isTemplate'] = template_checked
  287. // // 开启存为模板开关执行
  288. callback(newValues)
  289. })
  290. }, [form, imgMaterialConfig, imgListMaterialConfig, videoMaterialConfig, queryForm, template_checked, adcreative_template, isShowSc])
  291. // 获取创意形式列表
  292. useEffect(() => {
  293. if (siteSet?.length > 0 && promotedObjectType) {
  294. getAdcreativeTemplateList.run({
  295. siteSet,
  296. promotedObjectType,
  297. campaignType: 'CAMPAIGN_TYPE_NORMAL',
  298. }).then(res => {
  299. let newArr: any = []
  300. let newData: any[] = []
  301. // 过滤掉相同的和即将下线的
  302. if (!res) {
  303. return
  304. }
  305. //
  306. Object.values(res)?.forEach((arr: any) => {
  307. newData.push(arr)
  308. Array.isArray(arr) && arr?.forEach((item: any) => {
  309. if (newArr.length > 0) {//假如已存在ID,需要过滤相同
  310. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {//不重复的添加
  311. newArr.push(item)
  312. }
  313. } else {//不存在ID直接过滤掉即将下线的
  314. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
  315. newArr.push(item)
  316. }
  317. }
  318. })
  319. })
  320. /*****暂时排除激励和banner有问题******/
  321. if (siteSet.some((i: string) => i === 'SITE_SET_MOMENTS')) {
  322. newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 910 && item.adcreativeTemplateId !== 925 && item.adcreativeTemplateId !== 2107 && item.adcreativeTemplateId !== 2109)
  323. }
  324. /*****暂时排除出框形态 视频合约广告******/
  325. if (siteSet.some((i: string) => i === 'SITE_SET_WECHAT')) {
  326. newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 1945)
  327. }
  328. let newArr1: any[] = []
  329. let newArr2: any[] = []
  330. newArr?.forEach((arr: { adcreativeTemplateId: any, isGeneral?: boolean }) => {
  331. if (newData.every((item: { adcreativeTemplateId: any }[]) => item.find(i => i.adcreativeTemplateId === arr.adcreativeTemplateId))) {
  332. newArr1.push({ ...arr, isGeneral: true })
  333. } else {
  334. newArr2.push(arr)
  335. }
  336. })
  337. set_adcreative_template_list([...newArr1, ...newArr2])
  338. })
  339. }
  340. }, [siteSet, promotedObjectType])
  341. // 获取创意形式详情
  342. const getTemplate = (id: any, ok?: any) => {
  343. // CAMPAIGN_TYPE_NORMAL
  344. if (siteSet?.length > 0 && promotedObjectType && id) {
  345. if (id) {
  346. getAdcreativeTemplate.run({
  347. siteSet,
  348. promotedObjectType,
  349. adcreativeTemplateId: id
  350. }).then(res => {
  351. if (res?.length > 0) {
  352. form.setFieldsValue({ adcreativeName: res[0]?.adcreativeTemplateAppellation + '_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId })
  353. let adcreativeElements = res[0]?.adcreativeElements || []
  354. let elementStoryData = adcreativeElements?.find((item: { name: string }) => item.name === 'element_story')
  355. if (elementStoryData) {
  356. let imgageData = adcreativeElements?.find((item: { name: string }) => item.name === 'image')
  357. if (imgageData) {
  358. elementStoryData.restriction = imgageData.restriction
  359. adcreativeElements.splice(adcreativeElements?.findIndex((item: { name: string }) => item.name === 'image'), 1)
  360. }
  361. }
  362. set_adcreative_template(res[0])
  363. if (siteSet?.some((name: string) => name === 'SITE_SET_MOMENTS')) {
  364. let id = res[0].adcreativeTemplateId
  365. set_isShowSc(!!creativeConfig[id])//判定当前创意是否需要展示替换素材选项
  366. if (creativeConfig[id] && !ok) {//假如不等于回填元素的ID
  367. form.setFieldsValue({ overrideCanvasHeadOption: creativeConfig[id].overrideCanvasHeadOption[0] })
  368. }
  369. }
  370. templateChange(res[0], ok)
  371. }
  372. })
  373. }
  374. }
  375. }
  376. // 获取对应落地页按钮
  377. const pageTypeList = useMemo(() => {
  378. if (adcreativeTemplateId) {
  379. let arr: any = adcreative_template?.landingPageConfig?.supportPageTypeList
  380. return arr
  381. }
  382. return null
  383. }, [adcreativeTemplateId, adcreative_template])
  384. // 获取对应行动按钮数据
  385. const linkNameList = useMemo(() => {
  386. if (pageType) {
  387. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
  388. return arr
  389. }
  390. return null
  391. }, [pageType, pageTypeList])
  392. // 跳转落地页
  393. const linkPageList = useMemo(() => {
  394. if (pageType) {
  395. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  396. return arr
  397. }
  398. return null
  399. }, [pageType, pageTypeList])
  400. const typeChange = useCallback((adcreativeElementsType) => {
  401. if (adcreativeElementsType && adcreative_template_list?.length > 0) {
  402. let adcreativeTemplateIdArr = adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)
  403. console.log('typeChange====>', adcreativeTemplateIdArr[0].adcreativeTemplateId)
  404. getTemplate(adcreativeTemplateIdArr[0].adcreativeTemplateId)
  405. form.setFieldsValue({ adcreativeTemplateId: adcreativeTemplateIdArr[0].adcreativeTemplateId })
  406. }
  407. }, [adcreative_template_list])
  408. //每次选中创意设置该展示的界面
  409. const templateChange = (adcreative_template: any, ok?: any) => {
  410. let states = {
  411. kp_show: false,
  412. xd_show: true,
  413. sj_show: false,
  414. bq_show: false,
  415. sp_show: false
  416. }
  417. let values: any = { pageType: 'PAGE_TYPE_CANVAS_WECHAT', }
  418. if (adcreative_template) {
  419. let pageList = adcreative_template?.landingPageConfig?.supportPageTypeList?.filter((i: { description: string | string[] }) => i.description.includes('微信原生推广页'))//当前版本只获取微信原生页,后期改进
  420. let pageType = pageList?.length ? pageList[0]?.pageType : null
  421. //数据展示组件
  422. if (adcreative_template.adcreativeAttributes.some((item: { name: string }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
  423. let arr = adcreative_template.adcreativeAttributes?.filter((item: { name: string; }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')
  424. let newObj: any = {}
  425. arr.forEach((item: { propertyDetail: { enumDetail: { enumeration: any[] } }; name: string | number }) => {
  426. let arr: any[] = mySet(item.propertyDetail.enumDetail.enumeration)
  427. newObj[item.name] = arr
  428. })
  429. setConversionList(newObj)
  430. states = { ...states, sj_show: true }
  431. if (newObj.conversion_data_type) {
  432. values = { ...values, conversionDataType: newObj.conversion_data_type[0].value }
  433. }
  434. if (newObj.conversion_target_type) {
  435. values = { ...values, conversionTargetType: newObj.conversion_target_type[0].value }
  436. }
  437. }
  438. //行动按钮组件存在
  439. if (states.xd_show) {
  440. let supportLinkNameTypeData = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType
  441. let linkNameList = supportLinkNameTypeData?.list
  442. let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  443. if (linkNameList) {
  444. if (!ok) {
  445. let linkNameType = linkNameList[0]?.linkNameType
  446. let linkPageType = linkPageList?.some((i: { linkPageType: string }) => i.linkPageType === "LINK_PAGE_TYPE_CANVAS_WECHAT") ? "LINK_PAGE_TYPE_CANVAS_WECHAT" : linkPageList[0]?.linkPageType
  447. values = { ...values, linkNameType, linkPageType, actionBtn: true }
  448. }
  449. } else {
  450. states = { ...states, xd_show: false }
  451. }
  452. if (supportLinkNameTypeData?.required) {
  453. states = { ...states, xd_show: true }
  454. values = { ...values, actionBtn: true }
  455. setIsShowXd(true)
  456. } else {
  457. setIsShowXd(false)
  458. }
  459. }
  460. // 特殊行动按钮
  461. if (adcreative_template.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text') && !ok) {
  462. values = { ...values, buttonText: adcreative_template?.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text')?.enumProperty?.enumeration[0].value }
  463. }
  464. // 视频结束页 end_page
  465. if (adcreative_template.adcreativeElements.some((item: { name: string }) => item.name === 'end_page')) {
  466. // let endPageType =adcreative_template?.adcreativeElements?.filter(item=>item.name === 'end_page_type')[0]?.enumProperty?.enumeration
  467. if (!ok) {
  468. values = { ...values, endPageType: 'END_PAGE_AVATAR_NICKNAME_HIGHLIGHT' }
  469. }
  470. states = { ...states, sp_show: true }
  471. }
  472. setPupState(states)
  473. form.setFieldsValue(values)
  474. }
  475. }
  476. // 版位改变清空数据
  477. useEffect(() => {
  478. if (imgMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== imgMaterialConfig.adcreativeTemplateId) {
  479. setImgMaterialConfig({ ...imgMaterialConfig, adcreativeTemplateId: undefined, list: [] })
  480. setImgListMaterialConfig({ ...imgListMaterialConfig, adcreativeTemplateId: undefined, list: [] })
  481. }
  482. if (videoMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== videoMaterialConfig.adcreativeTemplateId) {
  483. setVideoMaterialConfig({ ...videoMaterialConfig, adcreativeTemplateId: undefined, list: [] })
  484. }
  485. }, [adcreativeTemplateId, imgMaterialConfig, videoMaterialConfig, imgListMaterialConfig])
  486. // 文案助手
  487. const textList = useCallback((arg: { maxTextLength: number, keyword?: string }) => {
  488. let { maxTextLength, keyword } = arg
  489. getTextLsit.run({ keyword: keyword || titles || description, maxTextLength })
  490. }, [titles, description])
  491. // 监听点击取消文案助手弹窗
  492. useEffect(() => {
  493. let modal = document.querySelector('.myModal')
  494. let onBiurdescription = (e: any) => {
  495. let d = document.querySelector('.my_description')
  496. let t = document.querySelector('.my_title')
  497. let p = document.querySelector('.my_endPageDesc')
  498. if (!d?.contains(e.target)) {
  499. setdescriptionshow(false)
  500. }
  501. if (!t?.contains(e.target)) {
  502. settitleshow(false)
  503. }
  504. if (!p?.contains(e.target)) {
  505. setendPageDescnshow(false)
  506. }
  507. }
  508. modal?.addEventListener('click', onBiurdescription)
  509. return () => {
  510. modal?.removeEventListener('click', onBiurdescription)
  511. }
  512. }, [])
  513. // 数据回填
  514. useEffect(() => {
  515. if (!infoSet && dataInfo && adcreative_template_list?.length > 0) {
  516. let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, profile, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
  517. let { description, imageUrl, title, videoUrl, imageUrlList, endPage, shortVideoStruct, brand, buttonText, elementStory } = adcreativeElements
  518. let obj: any = {
  519. adcreativeName,
  520. siteSet,
  521. promotedObjectType,
  522. adcreativeTemplateId,
  523. }
  524. getTemplate(adcreativeTemplateId, true)
  525. if ([720, 721, 618, 1708, 722, 1529].some(n => n === adcreativeTemplateId)) {
  526. obj = { ...obj, adcreativeElementsType: '视频' }
  527. } else {
  528. obj = { ...obj, adcreativeElementsType: '图片' }
  529. }
  530. if (conversionDataType) {
  531. obj = { ...obj, conversionDataType, dataShow: true }
  532. }
  533. if (conversionTargetType) {
  534. obj = { ...obj, conversionTargetType, dataShow: true }
  535. }
  536. if (linkNameType) {
  537. obj = { ...obj, linkNameType, actionBtn: true }
  538. }
  539. if (linkPageType) {
  540. obj = { ...obj, linkPageType, actionBtn: true }
  541. }
  542. if (pageType) {
  543. obj = { ...obj, pageType }
  544. }
  545. if (description) {
  546. obj = { ...obj, description }
  547. }
  548. if (title) {
  549. obj = { ...obj, title }
  550. }
  551. if (endPage) {
  552. obj = { ...obj, videoOver: true, ...endPage }
  553. }
  554. if (overrideCanvasHeadOption) {
  555. obj = { ...obj, overrideCanvasHeadOption }
  556. }
  557. if (linkPageSpec?.pageUrl) {
  558. obj = { ...obj, pageUrl: linkPageSpec?.pageUrl }
  559. }
  560. if (linkPageSpec?.miniProgramSpec && linkPageSpec?.miniProgramSpec?.miniProgramPath) {
  561. obj = { ...obj, miniProgramPath: linkPageSpec?.miniProgramSpec?.miniProgramPath, miniProgramId: linkPageSpec?.miniProgramSpec?.miniProgramId }
  562. }
  563. if (brand && brand.brandImgUrl && brand.brandName) {
  564. obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
  565. }
  566. if (profile && profile.headImageUrl && profile.profileName && profile.description) {
  567. obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description }
  568. }
  569. if (buttonText) {
  570. obj = { ...obj, buttonText }
  571. }
  572. if (videoUrl) {
  573. setVideoMaterialConfig({
  574. cloudSize: [],
  575. list: [{ url: videoUrl }],
  576. max: 1,
  577. type: 'video',
  578. adcreativeTemplateId
  579. })
  580. obj = { ...obj, video: videoUrl }
  581. }
  582. if (imageUrl) {
  583. setImgMaterialConfig({
  584. cloudSize: [],
  585. list: [{ url: imageUrl }],
  586. max: 1,
  587. type: 'image',
  588. adcreativeTemplateId
  589. })
  590. obj = { ...obj, image: imageUrl }
  591. }
  592. if (imageUrlList) {
  593. setImgListMaterialConfig({
  594. cloudSize: [],
  595. list: imageUrlList?.map((url: any) => ({ url })),
  596. max: imageUrlList.length,
  597. type: 'image_list',
  598. adcreativeTemplateId
  599. })
  600. obj = { ...obj, image_list: imageUrlList }
  601. }
  602. if (elementStory) {
  603. setImgMaterialConfig({
  604. cloudSize: [],
  605. list: elementStory?.map((item: { imageUrl: string }) => ({ url: item.imageUrl })),
  606. max: elementStory.length,
  607. type: 'element_story',
  608. adcreativeTemplateId
  609. })
  610. obj = { ...obj, element_story: imageUrlList }
  611. }
  612. if (shortVideoStruct) {
  613. setVideoMaterialConfig({
  614. cloudSize: [],
  615. list: [{ url: shortVideoStruct.shortVideo1Url }],
  616. max: 1,
  617. type: 'short_video1',
  618. adcreativeTemplateId
  619. })
  620. obj = { ...obj, short_video1: shortVideoStruct.shortVideo1Url }
  621. }
  622. console.log('数据回填====>', obj)
  623. form.setFieldsValue(obj)
  624. set_infoSet(true)
  625. }
  626. // 不是数据回填首次打开界面选中视频
  627. if (!infoSet && !dataInfo && adcreative_template_list?.length > 0) {
  628. typeChange('视频')
  629. set_infoSet(true)
  630. }
  631. }, [dataInfo, adcreative_template_list, adcreative_template, infoSet])
  632. // 生成视频封面图
  633. const videoToImgs = useCallback(() => {
  634. if (videoMaterialConfig.list[0]) {
  635. set_videoImgsVisbile(true)
  636. // let url = videoMaterialConfig.list[0].url
  637. // fetch(url).then(res => res.blob()).then(async (blob) => {
  638. // let file = new File([blob], 'sp', { type: blob.type })
  639. // // let md5 = await getMD5(file)
  640. // let formData = new FormData()
  641. // formData.append('videoFile', file)
  642. // formData.append('number', '12')
  643. // getVideoCapture.run(formData).then(res => {
  644. // console.log(res)
  645. // })
  646. // })
  647. } else {
  648. message.warning('请先选择视频文件!!!')
  649. }
  650. }, [videoMaterialConfig.list])
  651. return <Modal
  652. visible={visible}
  653. title={type === 'add' ? '新建创意' : type === 'look' ? '创意详情' : '编辑创意'}
  654. onCancel={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}
  655. // onOk={handleOk}
  656. width={1200}
  657. confirmLoading={confirmLoading}
  658. footer={<Space>
  659. <Button onClick={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}>取消</Button>
  660. <Button type='primary' onClick={handleOk}>确定</Button>
  661. {<Checkbox checked={template_checked} onChange={(e) => {
  662. let checked = e.target.checked
  663. settemplate_checked(checked)
  664. }}>存为模板</Checkbox>}
  665. </Space>}
  666. className='myModal'
  667. {...arg}
  668. >
  669. <Form
  670. form={form}
  671. labelCol={{ span: 5 }}
  672. labelWrap={true}
  673. className='ad_form_style'
  674. initialValues={
  675. {
  676. adcreativeElementsType: '视频',
  677. }
  678. }
  679. >
  680. {/* ============================================================创意形式============================================================= */}
  681. <Divider orientation='center'>创意形式</Divider>
  682. {/* ============================================================创意形式============================================================= */}
  683. <Form.Item label={<strong>创意形式</strong>} name='adcreativeElementsType'>
  684. <Radio.Group onChange={(e) => {
  685. let value = e.target.value
  686. typeChange(value)
  687. }}>
  688. <Radio.Button value="视频">视频</Radio.Button>
  689. <Radio.Button value="图片">图片</Radio.Button>
  690. </Radio.Group>
  691. </Form.Item>
  692. {
  693. getAdcreativeTemplateList?.loading ? <Spin tip="Loading..." style={{ width: '100%' }}></Spin> :
  694. <>
  695. <Form.Item style={{ marginLeft: 177 }} name='adcreativeTemplateId'>
  696. <Radio.Group className={styles.adcreative_template} onChange={(e) => {
  697. let id = e.target.value
  698. getTemplate(id)
  699. }}>
  700. {adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType && item.supportBidModeList.includes(queryForm?.sysAdgroup?.bidMode))?.map((item: any) => {
  701. return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
  702. <div className={styles.adcreative_template_item}>
  703. {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
  704. <img src={item.adcreativeSampleImage} />
  705. <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
  706. <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
  707. </div>
  708. </Radio.Button>
  709. })}
  710. </Radio.Group>
  711. </Form.Item>
  712. {/* ============================================================创意内容============================================================= */}
  713. <Divider orientation='center'>创意内容</Divider>
  714. {/* =============================================================头像及昵称跳转页===================================================================== */}
  715. {queryForm.promotedObjectType === 'PROMOTED_OBJECT_TYPE_LEAD_AD' ? adcreative_template?.adcreativeAttributes?.find(item => item.name === 'profile_id') ? <Form.Item label={<strong>头像及昵称跳转页</strong>} name='profile' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
  716. <HeadNickJump />
  717. </Form.Item> : <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
  718. <BrandImage />
  719. </Form.Item> : null}
  720. {/* ============================================================素材============================================================= */}
  721. {/* 优先展示视频或图片,朋友圈常规不勾选使用外部素材替换内部,隐藏此选项,后期自动将落地页顶部素材添加进入 */}
  722. {((overrideCanvasHeadOption !== 'OPTION_CANVAS_OVERRIDE_CREATIVE') || siteSet.every((name: string) => name !== 'SITE_SET_MOMENTS')) && <div style={{ display: 'flex', flexFlow: 'column' }}>
  723. {adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image' || item.name === 'element_story').map(item => {
  724. return <Form.Item
  725. label={<strong>{item.description === '图片' && adcreative_template?.adcreativeElements?.some(item => item.name === 'video') ? '视频封面图' : item.description}</strong>}
  726. key={item.name}
  727. >
  728. {/* 视频 */}
  729. {(item.name === 'short_video1' || item.name === 'video') && <Form.Item
  730. noStyle
  731. rules={[{ required: true, message: '请选择素材!' }]}
  732. name={item.name}
  733. style={item.description === '图片' && adcreative_template?.adcreativeElements?.some(item => item.name === 'video') ? { order: 2 } : {}}
  734. >
  735. <div className={`${styles.box} ${styles.video}`} onClick={() => {
  736. init({ mediaType: 'VIDEO', cloudSize: adcreativeTemplateId === 1708 ? [[{ relation: '=', width: 1280, height: 720 }]] : [[{ relation: '=', width: item.restriction.videoRestriction.minWidth, height: item.restriction.videoRestriction.minHeight }]], maxSize: item.restriction.videoRestriction.fileSize * 1024 })
  737. setTimeout(() => {
  738. set_selectVideoVisible(true)
  739. setVideoMaterialConfig({
  740. ...videoMaterialConfig,
  741. type: item.name,
  742. max: 1,
  743. adcreativeTemplateId
  744. })
  745. }, 100)
  746. }}>
  747. <p>
  748. {videoMaterialConfig?.list[0] ? <video src={videoMaterialConfig?.list[0].url} controls /> : <>
  749. <span>{`推荐尺寸(${adcreativeTemplateId === 1708 ? 1280 : item.restriction.videoRestriction.minWidth} x ${adcreativeTemplateId === 1708 ? 720 : item.restriction.videoRestriction.minHeight})`}</span>
  750. <span>{`${item.restriction.videoRestriction.fileFormat?.map(str => str?.replace('MEDIA_TYPE_', ''))};< ${item.restriction.videoRestriction.fileSize / 1024}M;时长 ≥ ${item.restriction.videoRestriction.minDuration}s,≤ ${item.restriction.videoRestriction.maxDuration}s,必须带有声音`}</span>
  751. </>}
  752. </p>
  753. </div>
  754. </Form.Item>}
  755. {/* 单图 */}
  756. {item.name === 'image' && <Form.Item
  757. noStyle
  758. rules={[{ required: true, message: '请选择素材!' }]}
  759. name={item.name}
  760. style={item.description === '图片' && adcreative_template?.adcreativeElements?.some(item => item.name === 'video') ? { order: 2 } : {}}
  761. >
  762. <div className={`${styles.box} ${styles.image}`} onClick={() => {
  763. init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
  764. setimgType('single')
  765. setTimeout(() => {
  766. set_selectImgVisible(true)
  767. setImgMaterialConfig({
  768. ...imgMaterialConfig,
  769. type: item.name,
  770. max: 1,
  771. adcreativeTemplateId
  772. })
  773. }, 100)
  774. }}>
  775. <p>
  776. {imgMaterialConfig?.list[0] ? <img src={imgMaterialConfig?.list[0].url} /> : <>
  777. <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
  778. <span>{`${item.restriction.imageRestriction.fileFormat?.map(str => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
  779. </>}
  780. </p>
  781. </div>
  782. </Form.Item>}
  783. {/* 多图 */}
  784. {item.name === 'image_list' && <Form.Item
  785. noStyle
  786. rules={[{ required: true, message: '请选择素材!' }]}
  787. name={item.name}
  788. style={item.description === '图片' && adcreative_template?.adcreativeElements?.some(item => item.name === 'video') ? { order: 2 } : {}}
  789. >
  790. <div className={`${styles.box} ${item.arrayProperty.maxNumber >= 3 ? styles.image_list : styles.image}`} onClick={() => {
  791. init({ mediaType: 'IMG', num: item.arrayProperty.maxNumber, cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]], maxSize: item.restriction.imageRestriction.fileSize * 1024 })
  792. setimgType('many')
  793. setTimeout(() => {
  794. set_selectImgVisible(true)
  795. setImgListMaterialConfig({
  796. ...imgListMaterialConfig,
  797. type: item.name,
  798. max: item.arrayProperty.maxNumber,
  799. adcreativeTemplateId
  800. })
  801. }, 100)
  802. }}>
  803. {Array(item.arrayProperty.maxNumber).fill('').map((arr, index) => {
  804. return <p key={index}>
  805. {imgListMaterialConfig?.list[index] ? <img src={imgListMaterialConfig?.list[index].url} /> : <>
  806. <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
  807. <span>{`${item.restriction.imageRestriction.fileFormat?.map(str => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
  808. </>}
  809. </p>
  810. })}
  811. </div>
  812. </Form.Item>}
  813. {item.name === 'element_story' && <Form.Item
  814. noStyle
  815. rules={[{ required: true, message: '请选择素材!' }]}
  816. name={item.name}
  817. style={item.description === '图片' && adcreative_template?.adcreativeElements?.some(item => item.name === 'video') ? { order: 2 } : {}}
  818. >
  819. <div className={`${styles.box} ${item.arrayProperty.maxNumber >= 3 ? styles.image_list : styles.image}`} onClick={() => {
  820. init({ mediaType: 'IMG', num: item?.arrayProperty?.maxNumber, cloudSize: [[{ relation: '=', width: item?.restriction.imageRestriction?.width, height: item?.restriction.imageRestriction?.height }]], maxSize: item?.restriction?.imageRestriction?.fileSize * 1024 })
  821. setimgType('single')
  822. setTimeout(() => {
  823. set_selectImgVisible(true)
  824. setImgMaterialConfig({
  825. ...imgMaterialConfig,
  826. type: item.name,
  827. max: item.arrayProperty.maxNumber,
  828. adcreativeTemplateId
  829. })
  830. }, 100)
  831. }}>
  832. {Array(item.arrayProperty.maxNumber).fill('').map((arr, index) => {
  833. return <p key={index}>
  834. {imgMaterialConfig?.list[index] ? <img style={item?.restriction.imageRestriction?.width > item?.restriction.imageRestriction?.height ? { width: '100%', height: 'auto' } : {}} src={imgMaterialConfig?.list[index].url} /> : <>
  835. <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
  836. <span>{`${item.restriction.imageRestriction.fileFormat?.map(str => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
  837. </>}
  838. </p>
  839. })}
  840. </div>
  841. </Form.Item>}
  842. </Form.Item>
  843. })}
  844. </div>}
  845. {/* 标题 */}
  846. {
  847. adcreative_template?.adcreativeElements?.filter(item => item.name === 'title').map(item => {
  848. return <div key={item.fieldType}>
  849. <Form.Item label={<strong>{item.description}(选填)</strong>} className={'my_title'} >
  850. <Form.Item name={item.name} rules={[{ pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]} noStyle>
  851. <Input
  852. placeholder={'请输入' + item.description}
  853. style={{ width: 500 }}
  854. allowClear
  855. onFocus={() => {
  856. settitleshow(true)
  857. textList({ maxTextLength: item.restriction.textRestriction.maxLength })
  858. }}
  859. onChange={(e) => {
  860. let value = e.target.value
  861. textList({ maxTextLength: item.restriction.textRestriction.maxLength, keyword: value })
  862. }}
  863. />
  864. </Form.Item>
  865. <span>{`${titles?.length ?? 0}/${item.restriction.textRestriction.maxLength}`}</span>
  866. {titleShow && <List
  867. loading={getTextLsit?.loading}
  868. size="small"
  869. style={{ maxHeight: 300, overflowX: 'auto' }}
  870. bordered
  871. dataSource={getTextLsit?.data?.returnTexts}
  872. renderItem={(item: any) => <List.Item onClick={() => {
  873. form.setFieldsValue({ title: item.text })
  874. settitleshow(false)
  875. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  876. />}
  877. </Form.Item>
  878. </div>
  879. })
  880. }
  881. {/* 过滤了不必传和品牌名称,品牌标识图(外部传)短视频结构(组装使用) */}
  882. {adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'description').map(item => {
  883. let maxNum = adcreativeTemplateId === 1708 || adcreativeTemplateId === 1707 ? pupState.xd_show ? 10 : item.restriction.textRestriction.maxLength : item.restriction.textRestriction.maxLength
  884. return <div key={item.fieldType}>
  885. <Form.Item label={<strong>{item.description}</strong>} className={'my_description'}>
  886. <Form.Item name={item.name} noStyle rules={[{ required: true, pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${maxNum}}`)), message: '请输入正确的' + item.description }]}>
  887. <Input
  888. placeholder={'请输入' + item.description}
  889. style={{ width: 500 }}
  890. onFocus={() => {
  891. setdescriptionshow(true)
  892. textList({ maxTextLength: maxNum })
  893. }}
  894. onChange={(e) => {
  895. let value = e.target.value
  896. textList({ maxTextLength: maxNum, keyword: value })
  897. }}
  898. allowClear
  899. />
  900. </Form.Item>
  901. <span>{`${description?.length ?? 0}/${maxNum}`}</span>
  902. {descriptionShow && <List
  903. loading={getTextLsit?.loading}
  904. size="small"
  905. style={{ maxHeight: 300, overflowX: 'auto' }}
  906. bordered
  907. dataSource={getTextLsit?.data?.returnTexts}
  908. renderItem={(item: any) => <List.Item onClick={(e: any) => {
  909. form.setFieldsValue({ description: item.text })
  910. setdescriptionshow(false)
  911. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  912. />}
  913. </Form.Item>
  914. </div>
  915. })}
  916. {/* ============================================================落地页============================================================= */}
  917. {adcreativeTemplateId ? <Form.Item label={<strong>落地页</strong>} name='pageType' >
  918. <Radio.Group>
  919. {pageTypeList?.map((item: any) => {
  920. return <Radio.Button value={item.pageType} key={item.pageType} disabled={!item.description.includes('微信原生推广页')}>{item.description.includes('微信原生推广页') ? '微信原生推广页' : item.description}</Radio.Button>
  921. })}
  922. </Radio.Group>
  923. </Form.Item> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  924. <Empty description="请先选择创意形式" />
  925. </div>}
  926. {pageType === 'PAGE_TYPE_CANVAS_WECHAT' && isShowSc && <Form.Item label={<strong>素材选项</strong>} name='overrideCanvasHeadOption'>
  927. <Radio.Group >
  928. {adcreativeTemplateId && creativeConfig[adcreativeTemplateId]?.overrideCanvasHeadOption?.map((item: string | number) => {
  929. return <Radio value={item} key={item}>{overrideCanvasHeadOptionEnum[item]}</Radio>
  930. })}
  931. </Radio.Group>
  932. </Form.Item>}
  933. {/* ============================================================普通行动按钮============================================================= */}
  934. {pupState.xd_show && <Form.Item hidden={isShowXd} label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
  935. <Switch checkedChildren="开启" unCheckedChildren="关闭" disabled={isShowXd} />
  936. </Form.Item>}
  937. {actionBtn && <>
  938. <Form.Item name='linkNameType' label={<strong>按钮文案</strong>}>
  939. <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
  940. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  941. } allowClear>
  942. {linkNameList?.map((item: any) => {
  943. return <Select.Option value={item.linkNameType} key={item.linkNameType}>{item.description}</Select.Option>
  944. })}
  945. </Select>
  946. </Form.Item>
  947. <Form.Item label={<strong>跳转落地页</strong>}>
  948. <Form.Item name='linkPageType' noStyle>
  949. <Radio.Group style={{ display: 'flex' }}>
  950. {linkPageList?.map((item: { linkPageType: string; description: string; }, index: number) => {
  951. return <Radio.Button value={item.linkPageType} key={item.linkPageType} >{item.description}</Radio.Button>
  952. })}
  953. </Radio.Group>
  954. </Form.Item>
  955. {/* 自定义落地页地址 */}
  956. {linkPageType === "LINK_PAGE_TYPE_DEFAULT" && <Form.Item name='pageUrl' rules={[{ required: true, message: '请输入自定义落地页地址' }]} style={{ marginTop: 10, marginBottom: 0 }}>
  957. <Input placeholder='请输入自定义落地页地址' style={{ width: 300 }} />
  958. </Form.Item>}
  959. {/* 小程序 */}
  960. {
  961. linkPageType === "LINK_PAGE_TYPE_MINI_PROGRAM_WECHAT" && <Form.Item noStyle >
  962. <Form.Item rules={[{ required: true, message: '请输入小程序原始ID' }]} name='miniProgramId' style={{ marginTop: 10, marginBottom: 0 }} >
  963. <Input placeholder='请输入小程序原始ID' style={{ width: 300 }} />
  964. </Form.Item>
  965. <Form.Item rules={[{ required: true, message: '请输入小程序链接' }]} name='miniProgramPath' style={{ marginTop: 10, marginBottom: 0 }}>
  966. <Input placeholder='请输入小程序链接' style={{ width: 300 }} />
  967. </Form.Item>
  968. </Form.Item>
  969. }
  970. </Form.Item>
  971. {/* 落地页 */}
  972. </>}
  973. {/* ============================================================特殊行动按钮============================================================= */}
  974. {!pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
  975. <Form.Item valuePropName="checked" noStyle >
  976. <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={true} disabled defaultChecked={true} />
  977. </Form.Item>
  978. </Form.Item>}
  979. {!pupState.xd_show && adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
  980. <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
  981. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  982. } allowClear>
  983. {
  984. adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text')?.enumProperty?.enumeration?.map((item: any) => {
  985. return <Select.Option value={item.value} key={item.value}>{item.value}</Select.Option>
  986. })
  987. }
  988. </Select>
  989. </Form.Item>}
  990. {/* ============================================================数据展示============================================================= */}
  991. {pupState.sj_show && <Form.Item label={<strong>数据展示</strong>} name='dataShow' valuePropName="checked">
  992. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  993. </Form.Item>}
  994. {dataShow && <>
  995. <Form.Item name='conversionDataType' label={<strong>数据类型</strong>}>
  996. <Radio.Group>
  997. {
  998. conversionList?.conversion_data_type?.map((item: { value: string; description: string; }, index: number) => {
  999. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  1000. })
  1001. }
  1002. </Radio.Group>
  1003. </Form.Item>
  1004. {conversionList?.conversion_target_type && conversionDataType === 'CONVERSION_DATA_ADMETRIC' && <Form.Item name='conversionTargetType' label={<strong>转化行为</strong>}>
  1005. <Radio.Group>
  1006. {
  1007. conversionList?.conversion_target_type?.map((item: { value: string; description: string; }, index: number) => {
  1008. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  1009. })
  1010. }
  1011. </Radio.Group>
  1012. </Form.Item>}
  1013. </>}
  1014. {/* ============================================================视频结束页============================================================= */}
  1015. {pupState.sp_show && <Form.Item label={<strong>视频结束页</strong>} name='videoOver' valuePropName="checked">
  1016. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  1017. </Form.Item>}
  1018. {videoOver && <>
  1019. <Form.Item name='endPageType' label={<strong>视频结束页类型</strong>} >
  1020. <Radio.Group>
  1021. {adcreative_template?.adcreativeElements?.filter(item => item.name === 'end_page_type')[0]?.enumProperty?.enumeration?.map((item) => {
  1022. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  1023. })}
  1024. </Radio.Group>
  1025. </Form.Item>
  1026. <div className={'my_endPageDesc'} >
  1027. <Form.Item label={<strong>结束文案</strong>}>
  1028. <Form.Item name='endPageDesc' rules={[{ required: true, pattern: RegExp("^[^\\<\\>\\&'\\\"\\/\\x08\\x09\\x0A\\x0D\\\\]{1,12}$"), message: '请输入正确的结束页文案' }]} noStyle>
  1029. <Input
  1030. placeholder='请输入结束页文案'
  1031. style={{ width: 300 }}
  1032. onFocus={() => {
  1033. setendPageDescnshow(true)
  1034. textList({ maxTextLength: 12 })
  1035. }}
  1036. onChange={(e) => {
  1037. let value = e.target.value
  1038. textList({ maxTextLength: 12, keyword: value })
  1039. }}
  1040. allowClear
  1041. />
  1042. </Form.Item>
  1043. <span>{endPageDesc?.length || 0}/12</span>
  1044. {endPageDescShow && <List
  1045. loading={getTextLsit?.loading}
  1046. size="small"
  1047. style={{ maxHeight: 300, maxWidth: 300, overflowX: 'auto' }}
  1048. bordered
  1049. dataSource={getTextLsit?.data?.returnTexts}
  1050. renderItem={(item: any) => <List.Item onClick={(e: any) => {
  1051. form.setFieldsValue({ endPageDesc: item.text })
  1052. setendPageDescnshow(false)
  1053. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  1054. />}
  1055. </Form.Item>
  1056. </div>
  1057. </>}
  1058. </>
  1059. }
  1060. {/* ============================================================基本信息============================================================= */}
  1061. <Divider orientation='center'>基本信息</Divider>
  1062. {/* ============================================================创意名称============================================================= */}
  1063. <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
  1064. <Input placeholder='创意名称' style={{ width: 300 }} />
  1065. </Form.Item>
  1066. </Form>
  1067. {/* 选择图片素材 */}
  1068. {selectImgVisible && <SelectCloud
  1069. visible={selectImgVisible}
  1070. onClose={() => {
  1071. set_selectImgVisible(false)
  1072. }}
  1073. sliderImgContent={imgType === 'many' ? imgListMaterialConfig.list : imgMaterialConfig.list}
  1074. onChange={(content) => {
  1075. if (imgType === 'many') {
  1076. setImgListMaterialConfig({ ...imgListMaterialConfig, list: content })
  1077. if (content.length > 0) {
  1078. form.setFieldsValue({ [imgListMaterialConfig.type]: imgListMaterialConfig.type })
  1079. }
  1080. } else {
  1081. setImgMaterialConfig({ ...imgMaterialConfig, list: content })
  1082. if (content.length > 0) {
  1083. form.setFieldsValue({ [imgMaterialConfig.type]: imgMaterialConfig.type })
  1084. }
  1085. }
  1086. set_selectImgVisible(false)
  1087. }}
  1088. />}
  1089. {/* 选择视频素材 */}
  1090. {selectVideoVisible && <SelectCloud
  1091. visible={selectVideoVisible}
  1092. onClose={() => set_selectVideoVisible(false)}
  1093. sliderImgContent={videoMaterialConfig.list}
  1094. onChange={(content) => {
  1095. if (content.length > 0) {
  1096. form.setFieldsValue({ [videoMaterialConfig.type]: videoMaterialConfig.type })
  1097. }
  1098. setVideoMaterialConfig({ ...videoMaterialConfig, list: content })
  1099. set_selectVideoVisible(false)
  1100. }}
  1101. />}
  1102. {/* 视频封面图弹窗 */}
  1103. {videoImgsVisbile && <Modal
  1104. visible={videoImgsVisbile}
  1105. title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
  1106. onOk={() => {
  1107. if (videoImgs.activeUrl) {
  1108. setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
  1109. set_videoImgsVisbile(false)
  1110. } else {
  1111. message.error('请选择图片,获取使用取消按钮关闭弹窗!')
  1112. }
  1113. }}
  1114. onCancel={() => { set_videoImgsVisbile(false) }}
  1115. confirmLoading={getVideoCapture.loading}
  1116. width={600}
  1117. >
  1118. <Radio.Group className={styles.videoImgs} onChange={(e) => {
  1119. let url = e.target.value
  1120. set_videoImgs({ ...videoImgs, activeUrl: url })
  1121. }}>
  1122. {
  1123. videoImgs?.urlList?.map((item: any, index: number) => {
  1124. return <Radio.Button value={item} key={index}>
  1125. <Image src={item} preview={videoImgs.preview} />
  1126. </Radio.Button>
  1127. })
  1128. }
  1129. </Radio.Group>
  1130. </Modal>}
  1131. </Modal >
  1132. }
  1133. export default CreativePup