modal.tsx 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. import React, { useCallback, useEffect, useMemo, useState } from 'react'
  2. import { Modal, Form, Input, Divider, Select, Radio, Switch, Spin, List } from 'antd'
  3. import { SiteSetEnum, PromotedObjectType } from '@/services/launchAdq/enum'
  4. import { ModalConfig } from '.'
  5. import styles from './index.less'
  6. import { outAdcreativeTemplateIdFun } from '../adenum';
  7. import { useAjax } from '@/Hook/useAjax'
  8. import { getText, get_adcreative_template, get_adcreative_template_list } from '@/services/launchAdq/global'
  9. import { AdcreativeTemplate, AdcreativeTemplateList } from '@/services/launchAdq'
  10. import { mySet } from '@/utils/arrFn'
  11. import SelectCloud from '@/pages/launchSystemNew/components/selectCloud'
  12. import { useModel } from '@/.umi/plugin-model/useModel'
  13. interface Props {
  14. title?: string,
  15. visible: boolean,
  16. PupFn: (arg: ModalConfig) => void,
  17. callback: (params: any) => void,
  18. confirmLoading: boolean,
  19. type?: 'add' | 'look' | 'edit',//新增,查看,编辑
  20. dataInfo?: any
  21. }
  22. /**创意模板*/
  23. function CreativeModal(props: Props) {
  24. let { visible, confirmLoading, PupFn, callback ,type,dataInfo} = props
  25. const { init } = useModel('useLaunchAdq.useBdMediaPup')
  26. let arg = type === 'look' ? { footer: null } : {}
  27. // 请求
  28. const getAdcreativeTemplate = useAjax((params) => get_adcreative_template(params))
  29. const getAdcreativeTemplateList = useAjax((params) => get_adcreative_template_list(params))
  30. const getTextLsit = useAjax((params) => getText(params))
  31. // 变量
  32. const [adcreative_template, set_adcreative_template] = useState<AdcreativeTemplate>()
  33. const [adcreative_template_list, set_adcreative_template_list] = useState<AdcreativeTemplateList[]>([])
  34. const [selectImgVisible, set_selectImgVisible] = useState(false)
  35. const [descriptionShow, setdescriptionshow] = useState(false)
  36. const [endPageDescShow, setendPageDescnshow] = useState(false)
  37. const [titleShow, settitleshow] = useState(false)
  38. const [login,setlogin]=useState(true)
  39. const [form] = Form.useForm();
  40. const [pupState, setPupState] = useState({
  41. kp_show: false,
  42. xd_show: false,
  43. sj_show: false,
  44. bq_show: false,
  45. sp_show: false
  46. })
  47. const [materialConfig, setMaterialConfig] = useState<{ adcreativeTemplateId?: number, type: string, cloudSize: { relation: string, width: number, height: number }[], list: any[], max: number }>({
  48. type: '',//类型
  49. cloudSize: [],//素材搜索条件
  50. list: [],//素材
  51. max: 1,//素材数量
  52. })//素材配置
  53. const [conversionList, setConversionList] = useState<any>(null)
  54. let pageType = Form.useWatch('pageType', form)
  55. let adcreativeTemplateId = Form.useWatch('adcreativeTemplateId', form)
  56. let actionBtn = Form.useWatch('actionBtn', form)
  57. let siteSet = Form.useWatch('siteSet', form)
  58. let promotedObjectType = Form.useWatch('promotedObjectType', form)
  59. let adcreativeElementsType = Form.useWatch('adcreativeElementsType', form)
  60. let dataShow = Form.useWatch('dataShow', form)
  61. let conversionDataType = Form.useWatch('conversionDataType', form)
  62. let titles = Form.useWatch('title', form)
  63. let description = Form.useWatch('description', form)
  64. let videoOver = Form.useWatch('videoOver', form)
  65. let endPageDesc = Form.useWatch('endPageDesc', form)
  66. // 确定事件
  67. const handleOk = useCallback(() => {
  68. form.validateFields().then(values => {
  69. console.log('values=>1', values)
  70. let newValues = JSON.parse(JSON.stringify(values))
  71. for (let key in newValues) {
  72. switch (key) {
  73. case 'image'://图素材
  74. newValues.adcreativeElements = {
  75. ...newValues.adcreativeElements,
  76. imageUrl: materialConfig.list[0].url,
  77. }
  78. delete newValues[key]
  79. break;
  80. case 'video'://视频素材
  81. newValues.adcreativeElements = {
  82. ...newValues.adcreativeElements,
  83. videoUrl: materialConfig.list[0].url,
  84. }
  85. delete newValues[key]
  86. break;
  87. case 'image_list'://图素材
  88. newValues.adcreativeElements = {
  89. imageUrlList: materialConfig.list?.map(item=>item.url),
  90. description: newValues.description,
  91. }
  92. delete newValues[key]
  93. break;
  94. case 'short_video1'://视频素材
  95. // newValues.adcreativeElements = {
  96. // video: materialConfig.list[0].url,
  97. // description: newValues.description,
  98. // }
  99. // delete newValues[key]
  100. break;
  101. case 'description'://文案
  102. newValues.adcreativeElements = { ...newValues.adcreativeElements, description: newValues.description }
  103. break;
  104. case 'title'://文案
  105. newValues.adcreativeElements = { ...newValues.adcreativeElements, title: newValues.title }
  106. break;
  107. }
  108. }
  109. delete newValues.description //删除外层文案
  110. delete newValues.title //删除外层文案
  111. delete newValues.adcreativeElementsType //删除创意形式
  112. delete newValues.dataShow //删除数据开关
  113. delete newValues.actionBtn //删除行动开关
  114. console.log('newValues=>2', newValues)
  115. callback(newValues)
  116. })
  117. // PupFn({ visible: false })
  118. }, [form, materialConfig])
  119. // 获取创意形式列表
  120. useEffect(() => {
  121. if (siteSet?.length > 0 && promotedObjectType) {
  122. getAdcreativeTemplateList.run({
  123. siteSet,
  124. promotedObjectType,
  125. campaignType: 'CAMPAIGN_TYPE_NORMAL',
  126. }).then(res => {
  127. let newArr: any = []
  128. // 过滤掉相同的和即将下线的
  129. Object.values(res)?.forEach((arr: any) => {
  130. Array.isArray(arr) && arr?.forEach((item: any) => {
  131. if (newArr.length > 0) {
  132. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId) && newArr.every((i: { adcreativeTemplateId: any }) => i.adcreativeTemplateId !== item.adcreativeTemplateId)) {
  133. newArr.push(item)
  134. } else {
  135. // 找出通用创意
  136. newArr = newArr?.map((arr: { adcreativeTemplateId: any }) => {
  137. if (arr.adcreativeTemplateId === item.adcreativeTemplateId) {
  138. return { ...arr, isGeneral: true }
  139. }
  140. return arr
  141. })
  142. }
  143. } else {
  144. if (outAdcreativeTemplateIdFun(item.adcreativeTemplateId)) {
  145. newArr.push(item)
  146. }
  147. }
  148. })
  149. })
  150. set_adcreative_template_list(newArr)
  151. })
  152. }
  153. }, [siteSet, promotedObjectType, form])
  154. // 获取创意形式详情
  155. useEffect(() => {
  156. // CAMPAIGN_TYPE_NORMAL
  157. if (siteSet?.length > 0 && promotedObjectType && adcreativeTemplateId) {
  158. if (adcreativeTemplateId) {
  159. getAdcreativeTemplate.run({
  160. siteSet,
  161. promotedObjectType,
  162. adcreativeTemplateId
  163. }).then(res => {
  164. if (res?.length > 0) {
  165. set_adcreative_template(res[0])
  166. }
  167. })
  168. }
  169. }
  170. }, [siteSet, promotedObjectType, adcreativeTemplateId])
  171. // 获取对应落地页按钮
  172. const pageTypeList = useMemo(() => {
  173. if (adcreativeTemplateId) {
  174. let arr: any = adcreative_template?.landingPageConfig?.supportPageTypeList
  175. return arr
  176. }
  177. return null
  178. }, [adcreativeTemplateId, adcreative_template])
  179. // 获取对应行动按钮数据
  180. const linkNameList = useMemo(() => {
  181. if (pageType) {
  182. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
  183. return arr
  184. }
  185. return null
  186. }, [pageType, pageTypeList])
  187. // 跳转落地页
  188. const linkPageList = useMemo(() => {
  189. if (pageType) {
  190. let arr = (pageTypeList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  191. return arr
  192. }
  193. return null
  194. }, [pageType, pageTypeList])
  195. // 切换创意形式默认选中第一个
  196. useEffect(() => {
  197. // 设置默认选中第一个
  198. if (adcreativeElementsType && adcreative_template_list?.length > 0) {
  199. let adcreativeTemplateIdArr = adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)
  200. form.setFieldsValue({ adcreativeTemplateId: adcreativeTemplateIdArr[0].adcreativeTemplateId })
  201. }
  202. }, [adcreativeElementsType, adcreative_template_list])
  203. //每次选中创意设置该展示的界面
  204. useEffect(() => {
  205. let states = {
  206. kp_show: false,
  207. xd_show: true,
  208. sj_show: false,
  209. bq_show: false,
  210. sp_show: false
  211. }
  212. let values: any = { pageType: 'PAGE_TYPE_CANVAS_WECHAT', }
  213. if (adcreative_template) {
  214. let pageList = adcreative_template?.landingPageConfig?.supportPageTypeList
  215. let pageType = pageList?.length ? pageList[0]?.pageType : null
  216. //数据展示组件
  217. if (adcreative_template.adcreativeAttributes.some(item => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')) {
  218. let arr = adcreative_template.adcreativeAttributes?.filter((item: { name: string; }) => item.name === 'conversion_data_type' || item.name === 'conversion_target_type')
  219. let newObj: any = {}
  220. arr.forEach((item) => {
  221. let arr: any[] = mySet(item.propertyDetail.enumDetail.enumeration)
  222. newObj[item.name] = arr
  223. })
  224. setConversionList(newObj)
  225. states = { ...states, sj_show: true }
  226. values = { ...values, conversionDataType: newObj.conversion_data_type[0].value, conversionTargetType: newObj.conversion_target_type[0].value }
  227. }
  228. //行动按钮组件存在
  229. if (states.xd_show) {
  230. let linkNameList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkNameType?.list
  231. let linkPageList = (pageList?.filter((item: { pageType: any; }) => item.pageType === pageType)[0] as any)?.supportLinkPageType?.list
  232. if (linkNameList) {
  233. let linkNameType = linkNameList[0]?.linkNameType
  234. let linkPageType = linkPageList[0]?.linkPageType
  235. values = { ...values, linkNameType, linkPageType }
  236. } else {
  237. states = { ...states, xd_show: false }
  238. }
  239. }
  240. // 视频结束页 end_page
  241. if (adcreative_template.adcreativeElements.some(item => item.name === 'end_page')) {
  242. // let endPageType =adcreative_template?.adcreativeElements?.filter(item=>item.name === 'end_page_type')[0]?.enumProperty?.enumeration
  243. values = { ...values, endPageType: 'END_PAGE_AVATAR_NICKNAME_HIGHLIGHT' }
  244. states = { ...states, sp_show: true }
  245. }
  246. setPupState(states)
  247. form.setFieldsValue(values)
  248. }
  249. }, [adcreative_template])
  250. // 版位改变清空数据
  251. useEffect(() => {
  252. if (materialConfig.adcreativeTemplateId && adcreativeTemplateId !== materialConfig.adcreativeTemplateId) {
  253. setMaterialConfig({ ...materialConfig, adcreativeTemplateId: undefined, list: [] })
  254. }
  255. }, [adcreativeTemplateId, materialConfig])
  256. // 文案助手
  257. const textList = useCallback((arg: { maxTextLength: number, keyword?: string }) => {
  258. let { maxTextLength, keyword } = arg
  259. getTextLsit.run({ keyword: keyword || titles || description, maxTextLength })
  260. }, [titles, description])
  261. // 监听点击取消文案助手弹窗
  262. useEffect(() => {
  263. let modal = document.querySelector('.myModal')
  264. let onBiurdescription = (e: any) => {
  265. let d = document.querySelector('.my_description')
  266. let t = document.querySelector('.my_title')
  267. let p = document.querySelector('.my_endPageDesc')
  268. if (!d?.contains(e.target)) {
  269. setdescriptionshow(false)
  270. }
  271. if (!t?.contains(e.target)) {
  272. settitleshow(false)
  273. }
  274. if (!p?.contains(e.target)) {
  275. setendPageDescnshow(false)
  276. }
  277. }
  278. modal?.addEventListener('click', onBiurdescription)
  279. return () => {
  280. modal?.removeEventListener('click', onBiurdescription)
  281. }
  282. }, [])
  283. // 数据回填
  284. useEffect(()=>{
  285. if(dataInfo && adcreative_template_list?.length >0 && adcreative_template){
  286. let {adcreativeName,adcreativeTemplateId,conversionDataType,conversionTargetType,linkNameType,linkPageType,pageType,promotedObjectType,siteSet,adcreativeElements} = dataInfo
  287. let {description,imageUrl,title,videoUrl,imageUrlList} = adcreativeElements
  288. let obj:any={
  289. adcreativeName,
  290. siteSet,
  291. promotedObjectType,
  292. adcreativeTemplateId,
  293. }
  294. if([720,721,618,1708].some(n=>n === adcreativeTemplateId)){
  295. obj={...obj,adcreativeElementsType:'视频'}
  296. }else{
  297. obj={...obj,adcreativeElementsType:'图片'}
  298. }
  299. if(conversionDataType){
  300. obj={...obj,conversionDataType,dataShow:true}
  301. }
  302. if(conversionTargetType){
  303. obj={...obj,conversionTargetType,dataShow:true}
  304. }
  305. if(linkNameType){
  306. obj={...obj,linkNameType,actionBtn:true}
  307. }
  308. if(linkPageType){
  309. obj={...obj,linkPageType,actionBtn:true}
  310. }
  311. if(pageType){
  312. obj={...obj,pageType}
  313. }
  314. if(description){
  315. obj={...obj,description}
  316. }
  317. if(title){
  318. obj={...obj,title}
  319. }
  320. if(videoUrl){
  321. setMaterialConfig({
  322. cloudSize:[],
  323. list:[{url:videoUrl}],
  324. max:1,
  325. type:'video',
  326. adcreativeTemplateId
  327. })
  328. }
  329. if(imageUrl){
  330. setMaterialConfig({
  331. cloudSize:[],
  332. list:[{url:imageUrl}],
  333. max:1,
  334. type:'image',
  335. adcreativeTemplateId
  336. })
  337. }
  338. if(imageUrlList){
  339. setMaterialConfig({
  340. cloudSize:[],
  341. list:imageUrlList?.map((url: any)=>({url})),
  342. max:imageUrlList.length,
  343. type:'image_list',
  344. adcreativeTemplateId
  345. })
  346. }
  347. form.setFieldsValue(obj)
  348. }
  349. },[dataInfo,adcreative_template_list,adcreative_template])
  350. return <Modal
  351. visible={visible}
  352. title={type === 'add' ? '新建创意' : type === 'look' ? '创意详情' : '编辑创意'}
  353. onCancel={() => { PupFn({ visible: false,dataInfo:null,type:'add' }) }}
  354. onOk={handleOk}
  355. width={1200}
  356. confirmLoading={confirmLoading}
  357. className='myModal'
  358. {...arg}
  359. >
  360. <Form
  361. form={form}
  362. labelCol={{ span: 5 }}
  363. labelWrap={true}
  364. initialValues={
  365. {
  366. adcreativeElementsType: '视频',
  367. promotedObjectType: 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT',
  368. siteSet: ['SITE_SET_MOMENTS', 'SITE_SET_WECHAT'],
  369. // actionBtn: false,//行动按钮
  370. // dataShow: false,//数据展示
  371. }
  372. }
  373. >
  374. {/* ============================================================基本信息============================================================= */}
  375. <Divider orientation='center'>基本信息</Divider>
  376. {/* ============================================================创意名称============================================================= */}
  377. <Form.Item label={<strong>创意名称</strong>} name='adcreativeName' rules={[{ required: true, message: '请输入广告名称!' }]}>
  378. <Input placeholder='创意名称' style={{ width: 300 }} />
  379. </Form.Item>
  380. {/* ============================================================推广目标类型============================================================= */}
  381. <Form.Item label={<strong>推广目标类型</strong>} name='promotedObjectType' rules={[{ required: true, message: '请选择推广告推广目标类型!' }]}>
  382. <Select style={{ width: 300 }} showSearch filterOption={(input, option) =>
  383. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  384. } allowClear>
  385. {
  386. Object.keys(PromotedObjectType).map(key => {
  387. return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
  388. })
  389. }
  390. </Select>
  391. </Form.Item>
  392. {/* ============================================================投放版位============================================================= */}
  393. <Form.Item label={<strong>投放版位</strong>} name='siteSet' rules={[{ required: true, message: '请输入选择广告版位!' }]}>
  394. <Select mode='multiple' style={{ width: 300 }} allowClear>
  395. {
  396. Object.keys(SiteSetEnum).map(key => {
  397. return <Select.Option value={key} key={key}>{SiteSetEnum[key]}</Select.Option>
  398. })
  399. }
  400. </Select>
  401. </Form.Item>
  402. {/* ============================================================创意形式============================================================= */}
  403. <Divider orientation='center'>创意形式</Divider>
  404. {/* ============================================================创意形式============================================================= */}
  405. <Form.Item label={<strong>创意形式</strong>} name='adcreativeElementsType'>
  406. <Radio.Group >
  407. <Radio.Button value="视频">视频</Radio.Button>
  408. <Radio.Button value="图片">图片</Radio.Button>
  409. </Radio.Group>
  410. </Form.Item>
  411. {
  412. getAdcreativeTemplateList?.loading ? <Spin tip="Loading..." style={{ width: '100%' }}></Spin> :
  413. <>
  414. <Form.Item style={{ marginLeft: 177 }} name='adcreativeTemplateId'>
  415. <Radio.Group className={styles.adcreative_template}>
  416. {
  417. adcreative_template_list?.filter(item => item.adcreativeTemplateStyle === adcreativeElementsType)?.map((item: any) => {
  418. return <Radio.Button value={item.adcreativeTemplateId} key={item.adcreativeTemplateId}>
  419. <div className={styles.adcreative_template_item}>
  420. {item.isGeneral && <span style={{ color: '#4080ff', fontSize: 10 }}>所选版位通投</span>}
  421. <img src={item.adcreativeSampleImage} />
  422. <span style={{fontSize:12,height:20,lineHeight:'20px'}}>{item.adcreativeTemplateAppellation}</span>
  423. <span style={{fontSize:12,height:20,lineHeight:'20px'}}>{item.adcreativeTemplateId}</span>
  424. </div>
  425. </Radio.Button>
  426. })
  427. }
  428. </Radio.Group>
  429. </Form.Item>
  430. {/* ============================================================创意内容============================================================= */}
  431. <Divider orientation='center'>创意内容</Divider>
  432. {/* ============================================================素材============================================================= */}
  433. {/* 优先展示视频或图片 */}
  434. {
  435. adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'image_list' || item.name === 'short_video1' || item.name === 'video' || item.name === 'image').map(item => {
  436. return <Form.Item label={<strong>{item.description}</strong>} rules={[{ required: true, message: '请选择素材!' }]} key={item.name} name={item.name}>
  437. {/* 视频 */}
  438. {
  439. (item.name === 'short_video1' || item.name === 'video') && <div className={`${styles.box} ${styles.video}`} onClick={() => {
  440. init({ mediaType: 'VIDEO', cloudSize: [[{ relation: '=', width: item.restriction.videoRestriction.minWidth, height: item.restriction.videoRestriction.minHeight }]] })
  441. setTimeout(() => {
  442. set_selectImgVisible(true)
  443. setMaterialConfig({
  444. ...materialConfig,
  445. type: item.name,
  446. max: 1,
  447. adcreativeTemplateId
  448. })
  449. }, 100)
  450. }}>
  451. <p>
  452. {
  453. materialConfig?.list[0] ? <video src={materialConfig?.list[0].url} /> : <>
  454. <span>{`推荐尺寸(${item.restriction.videoRestriction.minWidth} x ${item.restriction.videoRestriction.minHeight})`}</span>
  455. <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>
  456. </>
  457. }
  458. </p>
  459. </div>
  460. }
  461. {/* 单图 */}
  462. {
  463. item.name === 'image' && <div className={`${styles.box} ${styles.image}`} onClick={() => {
  464. init({ mediaType: 'IMG', cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]] })
  465. setTimeout(() => {
  466. set_selectImgVisible(true)
  467. setMaterialConfig({
  468. ...materialConfig,
  469. type: item.name,
  470. max: 1,
  471. adcreativeTemplateId
  472. })
  473. }, 100)
  474. }}>
  475. <p>
  476. {materialConfig?.list[0] ? <img src={materialConfig?.list[0].url} /> : <>
  477. <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
  478. <span>{`${item.restriction.imageRestriction.fileFormat?.map(str => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
  479. </>
  480. }
  481. </p>
  482. </div>
  483. }
  484. {/* 多图 */}
  485. {
  486. item.name === 'image_list' && <div className={`${styles.box} ${item.arrayProperty.maxNumber >= 3 ? styles.image_list : styles.image}`} onClick={() => {
  487. init({ mediaType: 'IMG', num: item.arrayProperty.maxNumber, cloudSize: [[{ relation: '=', width: item.restriction.imageRestriction.width, height: item.restriction.imageRestriction.height }]] })
  488. setTimeout(() => {
  489. set_selectImgVisible(true)
  490. setMaterialConfig({
  491. ...materialConfig,
  492. type: item.name,
  493. max: item.arrayProperty.maxNumber,
  494. adcreativeTemplateId
  495. })
  496. }, 100)
  497. }}>
  498. {
  499. Array(item.arrayProperty.maxNumber).fill('').map((arr, index) => {
  500. return <p key={index}>
  501. {
  502. materialConfig?.list[index] ? <img src={materialConfig?.list[index].url} /> : <>
  503. <span>{`推荐尺寸(${item.restriction.imageRestriction.width} x ${item.restriction.imageRestriction.height})`}</span>
  504. <span>{`${item.restriction.imageRestriction.fileFormat?.map(str => str?.replace('IMAGE_TYPE_', ''))};小于 ${item.restriction.imageRestriction.fileSize}KB`}</span>
  505. </>
  506. }
  507. </p>
  508. })
  509. }
  510. </div>
  511. }
  512. </Form.Item>
  513. })
  514. }
  515. {/* 标题 */}
  516. {
  517. adcreative_template?.adcreativeElements?.filter(item => item.name === 'title').map(item => {
  518. return <div key={item.fieldType}>
  519. <Form.Item label={<strong>{item.description}(选填)</strong>} className={'my_title'} >
  520. <Form.Item name={item.name} rules={[{ pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]} noStyle>
  521. <Input
  522. placeholder={'请输入' + item.description}
  523. style={{ width: 500 }}
  524. allowClear
  525. onFocus={() => {
  526. settitleshow(true)
  527. textList({ maxTextLength: item.restriction.textRestriction.maxLength })
  528. }}
  529. onChange={(e) => {
  530. let value = e.target.value
  531. textList({ maxTextLength: item.restriction.textRestriction.maxLength, keyword: value })
  532. }}
  533. />
  534. </Form.Item>
  535. <span>{`${titles?.length ?? 0}/${item.restriction.textRestriction.maxLength}`}</span>
  536. {
  537. titleShow && <List
  538. loading={getTextLsit?.loading}
  539. size="small"
  540. style={{ maxHeight: 300, overflowX: 'auto' }}
  541. bordered
  542. dataSource={getTextLsit?.data?.returnTexts}
  543. renderItem={(item: any) => <List.Item onClick={() => {
  544. form.setFieldsValue({ title: item.text })
  545. settitleshow(false)
  546. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  547. />
  548. }
  549. </Form.Item>
  550. </div>
  551. })
  552. }
  553. {//过滤了不必传和品牌名称,品牌标识图(外部传)短视频结构(组装使用)
  554. adcreative_template?.adcreativeElements?.filter(item => item.required && item.name === 'description').map(item => {
  555. return <div key={item.fieldType}>
  556. <Form.Item label={<strong>{item.description}</strong>} className={'my_description'}>
  557. <Form.Item name={item.name} noStyle rules={[{ required: true, pattern: RegExp(item.restriction.textRestriction.textPattern?.replace(/\+/ig, `{1,${item.restriction.textRestriction.maxLength}}`)), message: '请输入正确的' + item.description }]}>
  558. <Input
  559. placeholder={'请输入' + item.description}
  560. style={{ width: 500 }}
  561. onFocus={() => {
  562. setdescriptionshow(true)
  563. textList({ maxTextLength: item.restriction.textRestriction.maxLength })
  564. }}
  565. onChange={(e) => {
  566. let value = e.target.value
  567. textList({ maxTextLength: item.restriction.textRestriction.maxLength, keyword: value })
  568. }}
  569. allowClear
  570. />
  571. </Form.Item>
  572. <span>{`${description?.length ?? 0}/${item.restriction.textRestriction.maxLength}`}</span>
  573. {
  574. descriptionShow && <List
  575. loading={getTextLsit?.loading}
  576. size="small"
  577. style={{ maxHeight: 300, overflowX: 'auto' }}
  578. bordered
  579. dataSource={getTextLsit?.data?.returnTexts}
  580. renderItem={(item: any) => <List.Item onClick={(e: any) => {
  581. form.setFieldsValue({ description: item.text })
  582. setdescriptionshow(false)
  583. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  584. />
  585. }
  586. </Form.Item>
  587. </div>
  588. })
  589. }
  590. {/* ============================================================落地页============================================================= */}
  591. <Form.Item label={<strong>落地页</strong>} name='pageType'>
  592. <Radio.Group>
  593. {
  594. pageTypeList?.map((item: any) => {
  595. return <Radio.Button value={item.pageType} key={item.pageType} disabled={!item.description.includes('微信原生推广页')}>{item.description.includes('微信原生推广页') ? '微信原生推广页' : item.description}</Radio.Button>
  596. })
  597. }
  598. </Radio.Group>
  599. </Form.Item>
  600. {/* ============================================================行动按钮============================================================= */}
  601. {
  602. pupState.xd_show && <Form.Item label={<strong>行动按钮</strong>} name='actionBtn' valuePropName="checked">
  603. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  604. </Form.Item>
  605. }
  606. {
  607. actionBtn && <>
  608. <div style={{ display: 'flex' }}>
  609. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>按钮文案</strong></p>
  610. <Form.Item name='linkNameType'>
  611. <Select style={{ width: 200 }} showSearch filterOption={(input, option) =>
  612. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  613. } allowClear>
  614. {
  615. linkNameList?.map((item: any) => {
  616. return <Select.Option value={item.linkNameType} key={item.linkNameType}>{item.description}</Select.Option>
  617. })
  618. }
  619. </Select>
  620. </Form.Item>
  621. </div>
  622. <div style={{ display: 'flex' }}>
  623. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>跳转落地页</strong></p>
  624. <Form.Item name='linkPageType'>
  625. <Radio.Group>
  626. {
  627. linkPageList?.map((item: { linkPageType: string; description: string; }, index: number) => {
  628. return <Radio.Button value={item.linkPageType} key={item.linkPageType}>{item.description}</Radio.Button>
  629. })
  630. }
  631. </Radio.Group>
  632. </Form.Item>
  633. </div>
  634. </>
  635. }
  636. {/* ============================================================数据展示============================================================= */}
  637. {pupState.sj_show && <Form.Item label={<strong>数据展示</strong>} name='dataShow' valuePropName="checked">
  638. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  639. </Form.Item>}
  640. {
  641. dataShow && <>
  642. <div style={{ display: 'flex' }}>
  643. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>数据类型</strong></p>
  644. <Form.Item name='conversionDataType'>
  645. <Radio.Group>
  646. {
  647. conversionList?.conversion_data_type?.map((item: { value: string; description: string; }, index: number) => {
  648. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  649. })
  650. }
  651. </Radio.Group>
  652. </Form.Item>
  653. </div>
  654. {conversionDataType === 'CONVERSION_DATA_ADMETRIC' && <div style={{ display: 'flex' }}>
  655. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>转化行为</strong></p>
  656. <Form.Item name='conversionTargetType'>
  657. <Radio.Group>
  658. {
  659. conversionList?.conversion_target_type?.map((item: { value: string; description: string; }, index: number) => {
  660. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  661. })
  662. }
  663. </Radio.Group>
  664. </Form.Item>
  665. </div>}
  666. </>
  667. }
  668. {/* ============================================================视频结束页============================================================= */}
  669. {pupState.sp_show && <Form.Item label={<strong>视频结束页</strong>} name='videoOver' valuePropName="checked">
  670. <Switch checkedChildren="开启" unCheckedChildren="关闭" />
  671. </Form.Item>}
  672. {
  673. videoOver && <>
  674. <div style={{ display: 'flex', alignItems: 'center', marginBottom: 10 }}>
  675. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>视频结束页类型</strong></p>
  676. <Form.Item name='endPageType' style={{ marginBottom: 0 }} >
  677. <Radio.Group>
  678. {
  679. adcreative_template?.adcreativeElements?.filter(item => item.name === 'end_page_type')[0]?.enumProperty?.enumeration?.map((item) => {
  680. return <Radio.Button value={item.value} key={item.value}>{item.description}</Radio.Button>
  681. })
  682. }
  683. </Radio.Group>
  684. </Form.Item>
  685. </div>
  686. <div className={'my_endPageDesc'}>
  687. <div style={{ display: 'flex', alignItems: 'center' }} >
  688. <p style={{ marginBottom: 5, marginLeft: 177 }}><strong style={{ marginRight: 20 }}>结束文案</strong></p>
  689. <Form.Item name='endPageDesc' rules={[{ required: true, pattern: RegExp("^[^\\<\\>\\&'\\\"\\/\\x08\\x09\\x0A\\x0D\\\\]{1,12}$"), message: '请输入正确的结束页文案' }]} style={{ marginBottom: 0, marginRight: 10 }}>
  690. <Input
  691. placeholder='请输入结束页文案'
  692. style={{width:300}}
  693. onFocus={() => {
  694. setendPageDescnshow(true)
  695. textList({ maxTextLength: 12 })
  696. }}
  697. onChange={(e) => {
  698. let value = e.target.value
  699. textList({ maxTextLength: 12, keyword: value })
  700. }}
  701. allowClear
  702. />
  703. </Form.Item>
  704. <span>{endPageDesc?.length || 0}/12</span>
  705. </div>
  706. {
  707. endPageDescShow && <List
  708. loading={getTextLsit?.loading}
  709. size="small"
  710. style={{ maxHeight: 300, maxWidth: 300, overflowX: 'auto', marginLeft: 253 }}
  711. bordered
  712. dataSource={getTextLsit?.data?.returnTexts}
  713. renderItem={(item: any) => <List.Item onClick={(e: any) => {
  714. form.setFieldsValue({ endPageDesc: item.text })
  715. setendPageDescnshow(false)
  716. }}><span >{item.text}{item.tag && <span className={styles.crt}>{'CTR 高'}</span>}</span></List.Item>}
  717. />
  718. }
  719. </div>
  720. </>
  721. }
  722. </>
  723. }
  724. </Form>
  725. {/* 选择素材 */}
  726. {
  727. selectImgVisible && <SelectCloud
  728. visible={selectImgVisible}
  729. onClose={() => set_selectImgVisible(false)}
  730. sliderImgContent={materialConfig.list}
  731. onChange={(content) => {
  732. if (content.length > 0) {
  733. form.setFieldsValue({ [materialConfig.type]: materialConfig.type })
  734. }
  735. setMaterialConfig({ ...materialConfig, list: content })
  736. set_selectImgVisible(false)
  737. console.log(content)
  738. }} />
  739. }
  740. </Modal >
  741. }
  742. export default CreativeModal