index.tsx 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  2. import { Modal, Form, Input, Divider, Select, Radio, Switch, Spin, List, 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 { creativeConfig, overrideCanvasHeadOptionEnum } from './config'
  14. import BrandImage from './brandImage'
  15. import HeadNickJump from './headNickJump'
  16. import moment from 'moment'
  17. interface Props {
  18. queryForm: Partial<CreateAdProps>,
  19. title?: string,
  20. visible: boolean,
  21. PupFn: (arg: ModalConfig) => void,
  22. callback: (params: any, material: { label: string, name: string, restriction: any }[], textData: any[]) => void,
  23. confirmLoading?: boolean,
  24. type?: 'add' | 'look' | 'edit',//新增,查看,编辑
  25. dataInfo?: any
  26. }
  27. /**创意组模板*/
  28. function CreativePup(props: Props) {
  29. /***************************************/
  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. let arg = type === 'look' ? { footer: null } : {}
  36. // 请求
  37. const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
  38. const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
  39. const getTextLsit = useAjax((params) => getText(params))
  40. const getVideoCapture = useAjax((params) => get_tools_video_capture(params))
  41. // 变量
  42. const [adcreative_template, set_adcreative_template] = useState<AdcreativeTemplate>()
  43. const [adcreative_template_list, set_adcreative_template_list] = useState<AdcreativeTemplateList[]>([])
  44. const [selectImgVisible, set_selectImgVisible] = useState(false)
  45. const [selectVideoVisible, set_selectVideoVisible] = useState(false)
  46. const [videoImgsVisbile, set_videoImgsVisbile] = useState(false)
  47. const [endPageDescShow, setendPageDescnshow] = useState(false)
  48. const [materialData, setMaterialData] = useState<{ label: string, name: string, restriction: any }[]>([])
  49. const [textData, setTextData] = useState<any[]>([])
  50. const [isShowSc, set_isShowSc] = useState(false)//是否展示素材选项
  51. const [infoSet, set_infoSet] = useState(false)//回填设置已完成
  52. const [videoImgs, set_videoImgs] = useState<{//视频封面图设置
  53. activeUrl: string,//选中的视频封面图地址
  54. preview: boolean,//是否开启图片点击预览
  55. urlList: any[],//生成的视频封面列表
  56. }>({
  57. activeUrl: '',
  58. preview: false,
  59. urlList: [
  60. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/21D8D51AD98C4FF8BF41F1C2D28EA39F.jpg',
  61. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/80DBE1AB3EDE4E85ABAE5F1670D9FED0.jpg',
  62. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/BCB2DAB86BDB4549BCB8E493C4F29E82.jpg',
  63. 'https://test-adq-media.oss-cn-hangzhou.aliyuncs.com/image/545A4C2A5B874C82A9D1C0C063624AE5.jpg'
  64. ]
  65. })
  66. const [form] = Form.useForm();
  67. const [pupState, setPupState] = useState({
  68. kp_show: false,
  69. xd_show: false,
  70. sj_show: false,
  71. bq_show: false,
  72. sp_show: false
  73. })
  74. const [imgMaterialConfig, setImgMaterialConfig] = useState<{
  75. adcreativeTemplateId?: number,
  76. type: string,
  77. cloudSize: { relation: string, width: number, height: number }[],
  78. list: any[],
  79. max: number
  80. }>({
  81. type: '',//类型
  82. cloudSize: [],//素材搜索条件
  83. list: [],//素材
  84. max: 1,//素材数量
  85. })//图片素材配置
  86. const [videoMaterialConfig, setVideoMaterialConfig] = useState<{
  87. adcreativeTemplateId?: number,
  88. type: string,
  89. cloudSize: { relation: string, width: number, height: number }[],
  90. list: any[],
  91. max: number
  92. }>({
  93. type: '',//类型
  94. cloudSize: [],//素材搜索条件
  95. list: [],//素材
  96. max: 1,//素材数量
  97. })//图片素材配置
  98. const [conversionList, setConversionList] = useState<any>(null)
  99. let pageType = Form.useWatch('pageType', form)
  100. let adcreativeTemplateId = Form.useWatch('adcreativeTemplateId', form)
  101. let actionBtn = Form.useWatch('actionBtn', form)
  102. // let siteSet = Form.useWatch('siteSet', form)
  103. let overrideCanvasHeadOption = Form.useWatch('overrideCanvasHeadOption', form)
  104. let adcreativeElementsType = Form.useWatch('adcreativeElementsType', form)
  105. let dataShow = Form.useWatch('dataShow', form)
  106. let conversionDataType = Form.useWatch('conversionDataType', form)
  107. let titles = Form.useWatch('title', form)
  108. let description = Form.useWatch('description', form)
  109. let videoOver = Form.useWatch('videoOver', form)
  110. let endPageDesc = Form.useWatch('endPageDesc', form)
  111. let linkPageType = Form.useWatch('linkPageType', form)
  112. /***************************************/
  113. // 确定事件
  114. const handleOk = useCallback(() => {
  115. form.validateFields().then(values => {
  116. console.log('values=>1', values)
  117. let newValues = JSON.parse(JSON.stringify(values))
  118. for (let key in newValues) {
  119. switch (key) {
  120. case 'image'://图素材
  121. newValues.adcreativeElements = {
  122. ...newValues.adcreativeElements,
  123. imageUrl: imgMaterialConfig?.list[0]?.url,
  124. }
  125. delete newValues[key]
  126. break;
  127. case 'video'://视频素材
  128. newValues.adcreativeElements = {
  129. ...newValues.adcreativeElements,
  130. videoUrl: videoMaterialConfig?.list[0]?.url,
  131. }
  132. delete newValues[key]
  133. break;
  134. case 'image_list'://图素材
  135. newValues.adcreativeElements = {
  136. ...newValues.adcreativeElements,
  137. imageUrlList: imgMaterialConfig.list?.map(item => item.url),
  138. description: newValues.description,
  139. }
  140. delete newValues[key]
  141. break;
  142. case 'short_video1'://视频素材
  143. newValues.adcreativeElements = {
  144. ...newValues.adcreativeElements,
  145. shortVideoStruct: {
  146. shortVideo1Url: videoMaterialConfig?.list[0]?.url
  147. },
  148. description: newValues.description,
  149. }
  150. delete newValues[key]
  151. break;
  152. case 'description'://文案
  153. newValues.adcreativeElements = { ...newValues.adcreativeElements, description: newValues.description }
  154. break;
  155. case 'title'://文案
  156. newValues.adcreativeElements = { ...newValues.adcreativeElements, title: newValues.title }
  157. break;
  158. case 'endPageType'://视频结束l类型
  159. newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageType: newValues.endPageType } }
  160. delete newValues[key]
  161. break;
  162. case 'endPageDesc'://视频结束文案
  163. newValues.adcreativeElements = { ...newValues.adcreativeElements, endPage: { ...newValues.adcreativeElements.endPage, endPageDesc: newValues.endPageDesc } }
  164. delete newValues[key]
  165. break;
  166. case 'buttonText'://特殊行动按钮
  167. newValues.adcreativeElements = { ...newValues.adcreativeElements, buttonText: newValues.buttonText }
  168. delete newValues[key]
  169. break;
  170. case 'brand'://品牌形象
  171. newValues.adcreativeElements = {
  172. ...newValues.adcreativeElements, brand: {
  173. brandName: newValues.brand.split('_')[0],
  174. brandImgUrl: newValues.brand.split('_')[1]
  175. }
  176. }
  177. break;
  178. case 'profile':
  179. newValues.adcreativeElements = {
  180. ...newValues.adcreativeElements, brand: {
  181. brandName: newValues.profile.split('_')[0],
  182. brandImgUrl: newValues.profile.split('_')[1]
  183. }
  184. }
  185. newValues.profile = {
  186. headImageUrl: newValues.profile.split('_')[1],
  187. profileName: newValues.profile.split('_')[0],
  188. description: newValues.profile.split('_')[2]
  189. }
  190. break
  191. case 'pageUrl'://跳转落地页
  192. newValues.linkPageSpec = {
  193. ...newValues.linkPageSpec,
  194. pageUrl: newValues.pageUrl
  195. }
  196. delete newValues.pageUrl
  197. break;
  198. case 'miniProgramId':
  199. newValues.linkPageSpec = {
  200. ...newValues.linkPageSpec,
  201. miniProgramSpec: {
  202. miniProgramId: newValues.miniProgramId,
  203. miniProgramPath: newValues.miniProgramPath
  204. }
  205. }
  206. delete newValues.miniProgramId
  207. delete newValues.miniProgramPath
  208. break;
  209. }
  210. }
  211. if (!newValues.adcreativeElements) {
  212. newValues.adcreativeElements = {}
  213. }
  214. //假如不存在promotedObjectType
  215. if (!newValues?.promotedObjectType) {
  216. newValues['promotedObjectType'] = queryForm.promotedObjectType
  217. }
  218. // 假如不存在siteSet
  219. if (!newValues?.siteSet) {
  220. newValues['siteSet'] = queryForm.sysAdgroup.siteSet
  221. }
  222. delete newValues.description //删除外层文案
  223. delete newValues.title //删除外层文案
  224. delete newValues.adcreativeElementsType //删除创意形式
  225. delete newValues.dataShow //删除数据开关
  226. delete newValues.actionBtn //删除行动开关
  227. delete newValues.brand //品牌形象
  228. // 假如使用了落地页顶部素材替换外部素材
  229. if (newValues.overrideCanvasHeadOption === 'OPTION_CANVAS_OVERRIDE_CREATIVE') {
  230. console.log(adcreative_template?.adcreativeElements)
  231. adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').forEach(item => {
  232. switch (item.name) {
  233. case 'image'://图素材
  234. newValues.adcreativeElements = {
  235. ...newValues.adcreativeElements,
  236. imageUrl: '',
  237. }
  238. break;
  239. case 'video'://视频素材
  240. newValues.adcreativeElements = {
  241. ...newValues.adcreativeElements,
  242. videoUrl: '',
  243. }
  244. break;
  245. case 'image_list'://图素材
  246. newValues.adcreativeElements = {
  247. ...newValues.adcreativeElements,
  248. imageUrlList: [],
  249. }
  250. break;
  251. case 'short_video1'://视频素材
  252. newValues.adcreativeElements = {
  253. ...newValues.adcreativeElements,
  254. shortVideoStruct: {
  255. shortVideo1Url: ''
  256. },
  257. }
  258. break;
  259. }
  260. })
  261. }
  262. console.log('newValues=>2', newValues)
  263. newValues['isTemplate'] = template_checked
  264. // // 开启存为模板开关执行
  265. callback(newValues, materialData, textData)
  266. })
  267. }, [form, imgMaterialConfig, videoMaterialConfig, materialData, textData, queryForm, template_checked, adcreative_template, isShowSc])
  268. // 获取创意形式列表
  269. useEffect(() => {
  270. if (siteSet?.length > 0 && promotedObjectType) {
  271. getAdcreativeTemplateList.run({
  272. siteSet,
  273. promotedObjectType,
  274. campaignType: 'CAMPAIGN_TYPE_NORMAL',
  275. }).then(res => {
  276. let newArr: any = []
  277. // 过滤掉相同的和即将下线的
  278. if (!res) {
  279. return
  280. }
  281. //
  282. Object.values(res)?.forEach((arr: any) => {
  283. Array.isArray(arr) && arr?.forEach((item: any) => {
  284. if (newArr.length > 0) {//假如已存在ID,需要过滤相同
  285. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {//不重复的添加
  286. newArr.push(item)
  287. } else {
  288. // 找出通用创意
  289. newArr = newArr?.map((arr: { adcreativeTemplateId: any }) => {
  290. if (arr.adcreativeTemplateId === item.adcreativeTemplateId) {
  291. return { ...arr, isGeneral: true }
  292. }
  293. return arr
  294. })
  295. }
  296. } else {//不存在ID直接过滤掉即将下线的
  297. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
  298. newArr.push(item)
  299. }
  300. }
  301. })
  302. })
  303. /*****暂时排除激励和banner有问题******/
  304. if (siteSet.some((i: string) => i === 'SITE_SET_MOMENTS')) {
  305. newArr = newArr.filter((item: { adcreativeTemplateId: number }) => item.adcreativeTemplateId !== 910 && item.adcreativeTemplateId !== 925)
  306. }
  307. set_adcreative_template_list(newArr)
  308. })
  309. }
  310. }, [siteSet, promotedObjectType])
  311. // 获取创意形式详情
  312. const getTemplate = useCallback((id: any, ok?: any) => {
  313. // CAMPAIGN_TYPE_NORMAL
  314. if (siteSet?.length > 0 && promotedObjectType && id) {
  315. if (id) {
  316. getAdcreativeTemplate.run({
  317. siteSet,
  318. promotedObjectType,
  319. adcreativeTemplateId: id
  320. }).then(res => {
  321. if (res?.length > 0) {
  322. form.setFieldsValue({ adcreativeName: res[0]?.adcreativeTemplateAppellation + '_' + moment().format('YYYYMMDDhhmmss') + '_' + currentUser.userId })
  323. set_adcreative_template(res[0])
  324. if (siteSet?.some((name: string) => name === 'SITE_SET_MOMENTS')) {
  325. let id = res[0].adcreativeTemplateId
  326. set_isShowSc(!!creativeConfig[id])//判定当前创意是否需要展示替换素材选项
  327. if (creativeConfig[id] && !ok) {//假如不等于回填元素的ID
  328. let overrideCanvasHeadOption = creativeConfig[id].overrideCanvasHeadOption
  329. form.setFieldsValue({ overrideCanvasHeadOption: overrideCanvasHeadOption?.includes('OPTION_CREATIVE_OVERRIDE_CANVAS') ? 'OPTION_CREATIVE_OVERRIDE_CANVAS' : overrideCanvasHeadOption[0] })
  330. }
  331. }
  332. templateChange(res[0], ok)
  333. // 处理素材
  334. setMaterialData(res[0]?.adcreativeElements?.filter((item: any) => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').map((item: any) => {
  335. return {
  336. label: item.description === '图片' && res[0]?.adcreativeElements?.some((item: any) => item.name === 'video') ? '视频封面图' : item.description,
  337. name: item.name,
  338. restriction: item.restriction,
  339. arrayProperty: item?.arrayProperty
  340. }
  341. }))
  342. // 处理文案
  343. setTextData(res[0]?.adcreativeElements?.filter((item: any) => item.name === 'title' || (item.required && item.name === 'description')).map((item: any) => ({ ...item, pupState })))
  344. }
  345. })
  346. }
  347. }
  348. }, [siteSet, promotedObjectType, pupState])
  349. // 获取对应落地页按钮
  350. const pageTypeList = useMemo(() => {
  351. if (adcreativeTemplateId) {
  352. let arr: any = adcreative_template?.landingPageConfig?.supportPageTypeList
  353. return arr
  354. }
  355. return null
  356. }, [adcreativeTemplateId, adcreative_template])
  357. // 获取对应行动按钮数据
  358. const linkNameList = useMemo(() => {
  359. if (pageType) {
  360. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
  361. return arr
  362. }
  363. return null
  364. }, [pageType, pageTypeList])
  365. // 跳转落地页
  366. const linkPageList = useMemo(() => {
  367. if (pageType) {
  368. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  369. return arr
  370. }
  371. return null
  372. }, [pageType, pageTypeList])
  373. const typeChange = useCallback((adcreativeElementsType) => {
  374. if (adcreativeElementsType && adcreative_template_list?.length > 0) {
  375. let adcreativeTemplateIdArr = adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)
  376. console.log('typeChange====>', adcreativeTemplateIdArr[0].adcreativeTemplateId)
  377. getTemplate(adcreativeTemplateIdArr[0].adcreativeTemplateId)
  378. form.setFieldsValue({ adcreativeTemplateId: adcreativeTemplateIdArr[0].adcreativeTemplateId })
  379. }
  380. }, [adcreative_template_list])
  381. //每次选中创意设置该展示的界面
  382. const templateChange = useCallback((adcreative_template, ok?: any) => {
  383. let states = {
  384. kp_show: false,
  385. xd_show: true,
  386. sj_show: false,
  387. bq_show: false,
  388. sp_show: false
  389. }
  390. let values: any = { pageType: 'PAGE_TYPE_CANVAS_WECHAT', }
  391. if (adcreative_template) {
  392. let pageList = adcreative_template?.landingPageConfig?.supportPageTypeList?.filter((i: { description: string | string[] }) => i.description.includes('微信原生推广页'))//当前版本只获取微信原生页,后期改进
  393. let pageType = pageList?.length ? pageList[0]?.pageType : null
  394. //数据展示组件
  395. if (adcreative_template.adcreativeAttributes.some((item: { name: string }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
  396. let arr = adcreative_template.adcreativeAttributes?.filter((item: { name: string; }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')
  397. let newObj: any = {}
  398. arr.forEach((item: { propertyDetail: { enumDetail: { enumeration: any[] } }; name: string | number }) => {
  399. let arr: any[] = mySet(item.propertyDetail.enumDetail.enumeration)
  400. newObj[item.name] = arr
  401. })
  402. setConversionList(newObj)
  403. states = { ...states, sj_show: true }
  404. if (newObj.conversion_data_type) {
  405. values = { ...values, conversionDataType: newObj.conversion_data_type[0].value }
  406. }
  407. if (newObj.conversion_target_type) {
  408. values = { ...values, conversionTargetType: newObj.conversion_target_type[0].value }
  409. }
  410. }
  411. //行动按钮组件存在
  412. if (states.xd_show) {
  413. let linkNameList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
  414. let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  415. if (linkNameList && !linkPageType) {
  416. if (!ok) {
  417. let linkNameType = linkNameList[0]?.linkNameType
  418. let linkPageType = linkPageList?.some((i: { linkPageType: string }) => i.linkPageType === "LINK_PAGE_TYPE_CANVAS_WECHAT") ? "LINK_PAGE_TYPE_CANVAS_WECHAT" : linkPageList[0]?.linkPageType
  419. values = { ...values, linkNameType, linkPageType, actionBtn: true }
  420. }
  421. } else {
  422. states = { ...states, xd_show: false }
  423. }
  424. }
  425. // 特殊行动按钮
  426. if (adcreative_template.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text') && !ok) {
  427. values = { ...values, buttonText: adcreative_template?.adcreativeElements?.find((item: { name: string }) => item.name === 'button_text')?.enumProperty?.enumeration[0].value }
  428. }
  429. // 视频结束页 end_page
  430. if (adcreative_template.adcreativeElements.some((item: { name: string }) => item.name === 'end_page')) {
  431. // let endPageType =adcreative_template?.adcreativeElements?.filter(item=>item.name === 'end_page_type')[0]?.enumProperty?.enumeration
  432. if (!ok) {
  433. values = { ...values, endPageType: 'END_PAGE_AVATAR_NICKNAME_HIGHLIGHT' }
  434. }
  435. states = { ...states, sp_show: true }
  436. }
  437. setPupState(states)
  438. form.setFieldsValue(values)
  439. }
  440. }, [pageType, linkPageType])
  441. // 版位改变清空数据
  442. useEffect(() => {
  443. if (imgMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== imgMaterialConfig.adcreativeTemplateId) {
  444. setImgMaterialConfig({ ...imgMaterialConfig, adcreativeTemplateId: undefined, list: [] })
  445. }
  446. if (videoMaterialConfig.adcreativeTemplateId && adcreativeTemplateId !== videoMaterialConfig.adcreativeTemplateId) {
  447. setVideoMaterialConfig({ ...videoMaterialConfig, adcreativeTemplateId: undefined, list: [] })
  448. }
  449. }, [adcreativeTemplateId, imgMaterialConfig, videoMaterialConfig])
  450. // 文案助手
  451. const textList = useCallback((arg: { maxTextLength: number, keyword?: string }) => {
  452. let { maxTextLength, keyword } = arg
  453. getTextLsit.run({ keyword: keyword || titles || description, maxTextLength })
  454. }, [titles, description])
  455. // 监听点击取消文案助手弹窗
  456. useEffect(() => {
  457. let modal = document.querySelector('.myModal')
  458. let onBiurdescription = (e: any) => {
  459. let p = document.querySelector('.my_endPageDesc')
  460. if (!p?.contains(e.target)) {
  461. setendPageDescnshow(false)
  462. }
  463. }
  464. modal?.addEventListener('click', onBiurdescription)
  465. return () => {
  466. modal?.removeEventListener('click', onBiurdescription)
  467. }
  468. }, [])
  469. // 数据回填
  470. useEffect(() => {
  471. if (!infoSet && dataInfo && adcreative_template_list?.length > 0) {
  472. let { adcreativeName, adcreativeTemplateId, conversionDataType, conversionTargetType, linkNameType, linkPageType, pageType, promotedObjectType, siteSet, profile, adcreativeElements, overrideCanvasHeadOption, linkPageSpec } = dataInfo
  473. let { description, imageUrl, title, videoUrl, imageUrlList, endPage, shortVideoStruct, brand, buttonText } = adcreativeElements
  474. let obj: any = {
  475. adcreativeName,
  476. siteSet,
  477. promotedObjectType,
  478. adcreativeTemplateId,
  479. }
  480. getTemplate(adcreativeTemplateId, true)
  481. console.log(2222, dataInfo)
  482. if ([720, 721, 618, 1708, 722, 1529].some(n => n === adcreativeTemplateId)) {
  483. obj = { ...obj, adcreativeElementsType: '视频' }
  484. } else {
  485. obj = { ...obj, adcreativeElementsType: '图片' }
  486. }
  487. if (conversionDataType) {
  488. obj = { ...obj, conversionDataType, dataShow: true }
  489. }
  490. if (conversionTargetType) {
  491. obj = { ...obj, conversionTargetType, dataShow: true }
  492. }
  493. if (linkNameType) {
  494. obj = { ...obj, linkNameType, actionBtn: true }
  495. }
  496. if (linkPageType) {
  497. obj = { ...obj, linkPageType, actionBtn: true }
  498. }
  499. if (pageType) {
  500. obj = { ...obj, pageType }
  501. }
  502. if (description) {
  503. obj = { ...obj, description }
  504. }
  505. if (title) {
  506. obj = { ...obj, title }
  507. }
  508. if (endPage) {
  509. obj = { ...obj, videoOver: true, ...endPage }
  510. }
  511. if (overrideCanvasHeadOption) {
  512. obj = { ...obj, overrideCanvasHeadOption }
  513. }
  514. if (linkPageSpec?.pageUrl) {
  515. obj = { ...obj, pageUrl: linkPageSpec?.pageUrl }
  516. }
  517. if (linkPageSpec?.miniProgramSpec && linkPageSpec?.miniProgramSpec?.miniProgramPath) {
  518. obj = { ...obj, miniProgramPath: linkPageSpec?.miniProgramSpec?.miniProgramPath, miniProgramId: linkPageSpec?.miniProgramSpec?.miniProgramId }
  519. }
  520. if (brand && brand.brandImgUrl && brand.brandName) {
  521. obj = { ...obj, brand: brand.brandName + '_' + brand.brandImgUrl }
  522. }
  523. if (profile && profile.headImageUrl && profile.profileName && profile.description) {
  524. obj = { ...obj, profile: profile.profileName + '_' + profile.headImageUrl + '_' + profile.description }
  525. }
  526. if (buttonText) {
  527. obj = { ...obj, buttonText }
  528. }
  529. if (videoUrl) {
  530. setVideoMaterialConfig({
  531. cloudSize: [],
  532. list: [{ url: videoUrl }],
  533. max: 1,
  534. type: 'video',
  535. adcreativeTemplateId
  536. })
  537. obj = { ...obj, video: videoUrl }
  538. }
  539. if (imageUrl) {
  540. setImgMaterialConfig({
  541. cloudSize: [],
  542. list: [{ url: imageUrl }],
  543. max: 1,
  544. type: 'image',
  545. adcreativeTemplateId
  546. })
  547. obj = { ...obj, image: imageUrl }
  548. }
  549. if (imageUrlList) {
  550. setImgMaterialConfig({
  551. cloudSize: [],
  552. list: imageUrlList?.map((url: any) => ({ url })),
  553. max: imageUrlList.length,
  554. type: 'image_list',
  555. adcreativeTemplateId
  556. })
  557. obj = { ...obj, image_list: imageUrlList }
  558. }
  559. if (shortVideoStruct) {
  560. setVideoMaterialConfig({
  561. cloudSize: [],
  562. list: [{ url: shortVideoStruct.shortVideo1Url }],
  563. max: 1,
  564. type: 'short_video1',
  565. adcreativeTemplateId
  566. })
  567. obj = { ...obj, short_video1: shortVideoStruct.shortVideo1Url }
  568. }
  569. console.log('数据回填====>', obj)
  570. form.setFieldsValue(obj)
  571. set_infoSet(true)
  572. }
  573. // 不是数据回填首次打开界面选中视频
  574. if (!infoSet && !dataInfo && adcreative_template_list?.length > 0) {
  575. typeChange('视频')
  576. set_infoSet(true)
  577. }
  578. }, [dataInfo, adcreative_template_list, adcreative_template, infoSet])
  579. return <Modal
  580. visible={visible}
  581. title={type === 'add' ? '新建创意' : type === 'look' ? '创意详情' : '编辑创意'}
  582. onCancel={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}
  583. // onOk={handleOk}
  584. width={1200}
  585. confirmLoading={confirmLoading}
  586. footer={<Space>
  587. <Button onClick={() => { PupFn({ visible: false, dataInfo: null, type: 'add' }) }}>取消</Button>
  588. <Button type='primary' onClick={handleOk}>确定</Button>
  589. </Space>}
  590. className='myModal'
  591. {...arg}
  592. >
  593. <Form
  594. form={form}
  595. labelCol={{ span: 5 }}
  596. labelWrap={true}
  597. className='ad_form_style'
  598. initialValues={
  599. {
  600. adcreativeElementsType: '视频',
  601. }
  602. }
  603. >
  604. {/* ============================================================创意形式============================================================= */}
  605. <Divider orientation='center'>创意形式</Divider>
  606. {/* ============================================================创意形式============================================================= */}
  607. <Form.Item label={<strong>创意形式</strong>} name='adcreativeElementsType'>
  608. <Radio.Group onChange={(e) => {
  609. let value = e.target.value
  610. typeChange(value)
  611. }}>
  612. <Radio.Button value="视频">视频</Radio.Button>
  613. <Radio.Button value="图片">图片</Radio.Button>
  614. </Radio.Group>
  615. </Form.Item>
  616. {
  617. getAdcreativeTemplateList?.loading ? <Spin tip="Loading..." style={{ width: '100%' }}></Spin> :
  618. <>
  619. <Form.Item style={{ marginLeft: 177 }} name='adcreativeTemplateId'>
  620. <Radio.Group className={styles.adcreative_template} onChange={(e) => {
  621. let id = e.target.value
  622. getTemplate(id)
  623. }}>
  624. {
  625. adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)?.map((item: any) => {
  626. return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
  627. <div className={styles.adcreative_template_item}>
  628. {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
  629. <img src={item.adcreativeSampleImage} />
  630. <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateAppellation}</span>
  631. <span style={{ fontSize: 12, height: 20, lineHeight: '20px' }}>{item.adcreativeTemplateId}</span>
  632. </div>
  633. </Radio.Button>
  634. })
  635. }
  636. </Radio.Group>
  637. </Form.Item>
  638. {/* ============================================================创意内容============================================================= */}
  639. <Divider orientation='center'>创意内容</Divider>
  640. {/* =============================================================头像及昵称跳转页===================================================================== */}
  641. {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: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
  642. <HeadNickJump />
  643. </Form.Item> : <Form.Item label={<strong>品牌形象</strong>} name='brand' rules={[{ required: true, message: '请选择一个头像及昵称跳转页,与广告创意一起展示' }]}>
  644. <BrandImage />
  645. </Form.Item> : null}
  646. {/* ============================================================落地页============================================================= */}
  647. {adcreativeTemplateId ? <Form.Item label={<strong>落地页</strong>} name='pageType' >
  648. <Radio.Group>
  649. {
  650. pageTypeList?.map((item: any) => {
  651. return <Radio.Button value={item.pageType} key={item.pageType} disabled={!item.description.includes('微信原生推广页')}>{item.description.includes('微信原生推广页') ? '微信原生推广页' : item.description}</Radio.Button>
  652. })
  653. }
  654. </Radio.Group>
  655. </Form.Item> : <div style={{ minHeight: 400, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
  656. <Empty description="请先选择创意形式" />
  657. </div>}
  658. {
  659. pageType === 'PAGE_TYPE_CANVAS_WECHAT' && isShowSc && <Form.Item label={<strong>素材选项</strong>} name='overrideCanvasHeadOption'>
  660. <Radio.Group disabled>
  661. {
  662. adcreativeTemplateId && creativeConfig[adcreativeTemplateId]?.overrideCanvasHeadOption?.map((item: string | number) => {
  663. return <Radio value={item} key={item}>{overrideCanvasHeadOptionEnum[item]}</Radio>
  664. })
  665. }
  666. </Radio.Group>
  667. </Form.Item>
  668. }
  669. {/* ============================================================普通行动按钮============================================================= */}
  670. {
  671. pupState.xd_show && <Form.Item label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
  672. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  673. </Form.Item>
  674. }
  675. {
  676. actionBtn && <>
  677. <Form.Item name='linkNameType' label={<strong>按钮文案</strong>}>
  678. <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
  679. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  680. } allowClear>
  681. {
  682. linkNameList?.map((item: any) => {
  683. return <Select.Option value={item.linkNameType} key={item.linkNameType}>{item.description}</Select.Option>
  684. })
  685. }
  686. </Select>
  687. </Form.Item>
  688. <Form.Item label={<strong>跳转落地页</strong>}>
  689. <Form.Item name='linkPageType' noStyle>
  690. <Radio.Group style={{ display: 'flex' }}>
  691. {
  692. linkPageList?.map((item: { linkPageType: string; description: string; }, index: number) => {
  693. return <Radio.Button value={item.linkPageType} key={item.linkPageType} >{item.description}</Radio.Button>
  694. })
  695. }
  696. </Radio.Group>
  697. </Form.Item>
  698. {/* 自定义落地页地址 */}
  699. {linkPageType === "LINK_PAGE_TYPE_DEFAULT" && <Form.Item name='pageUrl' rules={[{ required: true, message: '请输入自定义落地页地址' }]} style={{ marginTop: 10, marginBottom: 0 }}>
  700. <Input placeholder='请输入自定义落地页地址' style={{ width: 300 }} />
  701. </Form.Item>}
  702. {/* 小程序 */}
  703. {
  704. linkPageType === "LINK_PAGE_TYPE_MINI_PROGRAM_WECHAT" && <Form.Item noStyle >
  705. <Form.Item rules={[{ required: true, message: '请输入小程序原始ID' }]} name='miniProgramId' style={{ marginTop: 10, marginBottom: 0 }} >
  706. <Input placeholder='请输入小程序原始ID' style={{ width: 300 }} />
  707. </Form.Item>
  708. <Form.Item rules={[{ required: true, message: '请输入小程序链接' }]} name='miniProgramPath' style={{ marginTop: 10, marginBottom: 0 }}>
  709. <Input placeholder='请输入小程序链接' style={{ width: 300 }} />
  710. </Form.Item>
  711. </Form.Item>
  712. }
  713. </Form.Item>
  714. {/* 落地页 */}
  715. </>
  716. }
  717. {/* ============================================================特殊行动按钮============================================================= */}
  718. {
  719. adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item label={<strong>行动按钮</strong>} >
  720. <Form.Item valuePropName="checked" noStyle >
  721. <Switch checkedChildren="开启" unCheckedChildren="关闭" checked={true} disabled defaultChecked={true} />
  722. </Form.Item>
  723. </Form.Item>
  724. }
  725. {
  726. adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text') && <Form.Item name='buttonText' label={<strong>按钮文案</strong>} rules={[{ required: true, message: '请选择按钮文案!' }]}>
  727. <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
  728. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  729. } allowClear>
  730. {
  731. adcreative_template?.adcreativeElements?.find(item => item.name === 'button_text')?.enumProperty?.enumeration?.map((item: any) => {
  732. return <Select.Option value={item.value} key={item.value}>{item.value}</Select.Option>
  733. })
  734. }
  735. </Select>
  736. </Form.Item>
  737. }
  738. {/* ============================================================数据展示============================================================= */}
  739. {pupState.sj_show && <Form.Item label={<strong>数据展示</strong>} name='dataShow' valuePropName="checked">
  740. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  741. </Form.Item>}
  742. {
  743. dataShow && <>
  744. <Form.Item name='conversionDataType' label={<strong>数据类型</strong>}>
  745. <Radio.Group>
  746. {
  747. conversionList?.conversion_data_type?.map((item: { value: string; description: string; }, index: number) => {
  748. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  749. })
  750. }
  751. </Radio.Group>
  752. </Form.Item>
  753. {conversionList?.conversion_target_type && conversionDataType === 'CONVERSION_DATA_ADMETRIC' && <Form.Item name='conversionTargetType' label={<strong>转化行为</strong>}>
  754. <Radio.Group>
  755. {
  756. conversionList?.conversion_target_type?.map((item: { value: string; description: string; }, index: number) => {
  757. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  758. })
  759. }
  760. </Radio.Group>
  761. </Form.Item>}
  762. </>
  763. }
  764. {/* ============================================================视频结束页============================================================= */}
  765. {pupState.sp_show && <Form.Item label={<strong>视频结束页</strong>} name='videoOver' valuePropName="checked">
  766. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  767. </Form.Item>}
  768. {
  769. videoOver && <>
  770. <Form.Item name='endPageType' label={<strong>视频结束页类型</strong>} >
  771. <Radio.Group>
  772. {
  773. adcreative_template?.adcreativeElements?.filter(item => item.name === 'end_page_type')[0]?.enumProperty?.enumeration?.map((item) => {
  774. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  775. })
  776. }
  777. </Radio.Group>
  778. </Form.Item>
  779. <div className={'my_endPageDesc'} >
  780. <Form.Item label={<strong>结束文案</strong>}>
  781. <Form.Item name='endPageDesc' rules={[{ required: true, pattern: RegExp("^[^\\<\\>\\&'\\\"\\/\\x08\\x09\\x0A\\x0D\\\\]{1,12}$"), message: '请输入正确的结束页文案' }]} noStyle>
  782. <Input
  783. placeholder='请输入结束页文案'
  784. style={{ width: 300 }}
  785. onFocus={() => {
  786. setendPageDescnshow(true)
  787. textList({ maxTextLength: 12 })
  788. }}
  789. onChange={(e) => {
  790. let value = e.target.value
  791. textList({ maxTextLength: 12, keyword: value })
  792. }}
  793. allowClear
  794. />
  795. </Form.Item>
  796. <span>{endPageDesc?.length || 0}/12</span>
  797. {
  798. endPageDescShow && <List
  799. loading={getTextLsit?.loading}
  800. size="small"
  801. style={{ maxHeight: 300, maxWidth: 300, overflowX: 'auto' }}
  802. bordered
  803. dataSource={getTextLsit?.data?.returnTexts}
  804. renderItem={(item: any) => <List.Item onClick={(e: any) => {
  805. form.setFieldsValue({ endPageDesc: item.text })
  806. setendPageDescnshow(false)
  807. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  808. />
  809. }
  810. </Form.Item>
  811. </div>
  812. </>
  813. }
  814. </>
  815. }
  816. {/* ============================================================基本信息============================================================= */}
  817. <Divider orientation='center'>基本信息</Divider>
  818. {/* ============================================================创意名称============================================================= */}
  819. <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
  820. <Input placeholder='创意名称' style={{ width: 300 }} />
  821. </Form.Item>
  822. </Form>
  823. {/* 选择图片素材 */}
  824. {
  825. selectImgVisible && <SelectCloud
  826. visible={selectImgVisible}
  827. onClose={() => {
  828. set_selectImgVisible(false)
  829. }}
  830. sliderImgContent={imgMaterialConfig.list}
  831. onChange={(content) => {
  832. if (content.length > 0) {
  833. form.setFieldsValue({ [imgMaterialConfig.type]: imgMaterialConfig.type })
  834. }
  835. setImgMaterialConfig({ ...imgMaterialConfig, list: content })
  836. set_selectImgVisible(false)
  837. }} />
  838. }
  839. {/* 选择视频素材 */}
  840. {
  841. selectVideoVisible && <SelectCloud
  842. visible={selectVideoVisible}
  843. onClose={() => set_selectVideoVisible(false)}
  844. sliderImgContent={videoMaterialConfig.list}
  845. onChange={(content) => {
  846. if (content.length > 0) {
  847. form.setFieldsValue({ [videoMaterialConfig.type]: videoMaterialConfig.type })
  848. }
  849. setVideoMaterialConfig({ ...videoMaterialConfig, list: content })
  850. set_selectVideoVisible(false)
  851. }} />
  852. }
  853. {/* 视频封面图弹窗 */}
  854. {
  855. videoImgsVisbile && <Modal
  856. visible={videoImgsVisbile}
  857. title={<div>生成封面图 <Switch checkedChildren="开启预览" unCheckedChildren="关闭预览" checked={videoImgs.preview} onChange={(checked) => { set_videoImgs({ ...videoImgs, preview: checked }) }} /></div>}
  858. onOk={() => {
  859. if (videoImgs.activeUrl) {
  860. setImgMaterialConfig({ ...imgMaterialConfig, list: [{ url: videoImgs.activeUrl }] })
  861. set_videoImgsVisbile(false)
  862. } else {
  863. message.error('请选择图片,获取使用取消按钮关闭弹窗!')
  864. }
  865. }}
  866. onCancel={() => { set_videoImgsVisbile(false) }}
  867. confirmLoading={getVideoCapture.loading}
  868. width={600}
  869. >
  870. <Radio.Group className={styles.videoImgs} onChange={(e) => {
  871. let url = e.target.value
  872. set_videoImgs({ ...videoImgs, activeUrl: url })
  873. }}>
  874. {
  875. videoImgs?.urlList?.map((item: any, index: number) => {
  876. return <Radio.Button value={item} key={index}>
  877. <Image src={item} preview={videoImgs.preview} />
  878. </Radio.Button>
  879. })
  880. }
  881. </Radio.Group>
  882. </Modal>
  883. }
  884. </Modal >
  885. }
  886. export default CreativePup