index.tsx 76 KB

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