creativeConversionAssistant.tsx 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. import { Card, Checkbox, Form, Radio, Select, Space, Switch, Tooltip } from "antd"
  2. import React, { useContext, useEffect, useMemo, useState } from "react"
  3. import { DispatchDynamic } from "./newDynamic";
  4. import style from '../index.less'
  5. import New1Radio from "@/pages/launchSystemV3/components/New1Radio";
  6. import { useUpdateEffect } from "ahooks";
  7. import { QuestionCircleFilled } from "@ant-design/icons";
  8. import { txtLength } from "@/utils/utils";
  9. import TextAideInput from "@/pages/launchSystemV3/components/TextAideInput";
  10. import styles from '../Material/index.less'
  11. import SelectLabel from "./SelectLable"
  12. import SelectCloudNew from "@/pages/launchSystemV3/material/cloudNew/selectCloudNew";
  13. import SelectBarrage from "./SelectBarrage";
  14. /**
  15. * 营销组件
  16. * @returns
  17. */
  18. const CreativeConversionAssistant: React.FC<{ automaticSiteEnabled?: boolean, putInType?: 'NOVEL' | 'GAME' }> = ({ automaticSiteEnabled, putInType }) => {
  19. /**************************************/
  20. const { creativeComponents, form, isUpdate, setIsUpdate } = useContext(DispatchDynamic)!;
  21. const textLinkShow = Form.useWatch('textLinkShow', form)
  22. const actionButtonShow = Form.useWatch('actionButtonShow', form)
  23. const showDataShow = Form.useWatch('showDataShow', form)
  24. const pageSpec = Form.useWatch('pageSpec', form)
  25. const linkNameType = Form.useWatch(['textLink', 'value', 'linkNameType'], form)
  26. const conversionDataType = Form.useWatch(['showData', 'value', 'conversionDataType'], form)
  27. const conversionTargetType = Form.useWatch(['showData', 'value', 'conversionTargetType'], form)
  28. const floatingZone = Form.useWatch('floatingZone', form)
  29. const floatingZoneSwitch = Form.useWatch(['floatingZone', 'value', 'floatingZoneSwitch'], form)
  30. const floatingZoneType = Form.useWatch(['floatingZone', 'value', 'floatingZoneType'], form)
  31. const image = Form.useWatch(['floatingZone', 'value', 'image'], form)
  32. const creativeTemplateId = Form.useWatch('creativeTemplateId', form)
  33. const cardType: string[] = Form.useWatch('cardType', form)
  34. const [cardData, setCardData] = useState<{ label: string, value: string, disabled?: boolean }[]>([])
  35. const [materialConfig, setMaterialConfig] = useState<{
  36. adcreativeTemplateId?: number,
  37. type: string,
  38. cloudSize: { relation: string, width: number, height: number }[],
  39. list: any[],
  40. index: number,
  41. max: number,
  42. sliderImgContent: any,
  43. isGroup?: boolean
  44. }>({
  45. type: '',//类型
  46. cloudSize: [],//素材搜索条件
  47. list: [],//素材
  48. index: 0, // 素材组下标
  49. max: 1,//素材数量
  50. sliderImgContent: undefined
  51. })//图片素材配置
  52. const [selectCloudData, setSelectCloudData] = useState<{
  53. defaultParams: {
  54. sizeQueries?: {
  55. width: number,
  56. height: number,
  57. relation: string
  58. }[],
  59. materialType: 'image' | 'video'
  60. fileSize: number
  61. }
  62. num: number
  63. }>()
  64. const [selectVideoVisible, setSelectVideoVisible] = useState(false)
  65. /**************************************/
  66. useUpdateEffect(() => {
  67. if (isUpdate) {
  68. setDefaultShouData()
  69. }
  70. }, [linkNameType, conversionDataType, conversionTargetType, showDataShow, creativeComponents?.show_data, isUpdate])
  71. const setDefaultShouData = () => {
  72. setIsUpdate(false)
  73. if (showDataShow && creativeComponents?.show_data) {
  74. let showData = creativeComponents?.show_data;
  75. let link_name_type = linkNameType || ''
  76. let conversionDataTypeData = showData?.children?.conversion_data_type
  77. let values: any = {}
  78. if (conversionDataTypeData) {
  79. let conversionDataTypeEnumeration = (conversionDataTypeData.enumProperty.enumeration as { value: string, category: string, description: string }[])
  80. .filter(item => item.category === link_name_type)
  81. .map(item => item.value)
  82. if (conversionDataTypeEnumeration.length === 0) {
  83. conversionDataTypeEnumeration = (conversionDataTypeData.enumProperty.enumeration as { value: string, category: string, description: string }[])
  84. .filter(item => item.category === '')
  85. .map(item => item.value)
  86. }
  87. if (!conversionDataTypeEnumeration.includes(conversionDataType)) {
  88. values.conversionDataType = conversionDataTypeEnumeration?.[0]
  89. }
  90. }
  91. let conversionTargetTypeData = showData?.children?.conversion_target_type
  92. if (conversionTargetTypeData) {
  93. let conversionTargetTypeEnumeration = (conversionTargetTypeData.enumProperty.enumeration as { value: string, category: string, description: string }[])
  94. .filter(item => item.category === link_name_type)
  95. .map(item => item.value)
  96. if (conversionTargetTypeEnumeration.length === 0) {
  97. conversionTargetTypeEnumeration = (conversionTargetTypeData.enumProperty.enumeration as { value: string, category: string, description: string }[])
  98. .filter(item => item.category === '')
  99. .map(item => item.value)
  100. }
  101. if (!conversionTargetTypeEnumeration.includes(conversionTargetType)) {
  102. values.conversionTargetType = conversionTargetTypeEnumeration?.[0]
  103. }
  104. }
  105. let valueKeys = Object.keys(values)
  106. if (valueKeys.length > 0) {
  107. if (!valueKeys.includes('conversionTargetType')) {
  108. values.conversionTargetType = conversionTargetType
  109. }
  110. if (!valueKeys.includes('conversionDataType')) {
  111. values.conversionDataType = conversionDataType
  112. }
  113. console.log('values--->', values)
  114. form.setFieldsValue({
  115. showData: {
  116. value: values
  117. }
  118. })
  119. }
  120. }
  121. }
  122. /** 微信文字链 */
  123. const textLinkContent = useMemo(() => {
  124. let textLink = creativeComponents?.text_link
  125. if (textLink && ![1707, 1708].includes(creativeTemplateId) && !automaticSiteEnabled) {
  126. let pageSpecPageType = pageSpec?.[0]?.pageType || 'PAGE_TYPE_WECHAT_CANVAS'
  127. let textLinkRequired, linkNameEnumeration: { label: string, value: string }[] = [];
  128. let linkNamePageType, linkNamePageTypeEnumeration: PULLIN.DataType[] = [];
  129. textLinkRequired = textLink.required
  130. let linkNameType = textLink.children.link_name_type
  131. linkNameEnumeration = (linkNameType.enumProperty.enumeration as { value: string, description: string }[]).map(item => ({ label: item.description, value: item.value }))
  132. linkNamePageType = textLink?.children?.page_type
  133. if (linkNamePageType) {
  134. linkNamePageTypeEnumeration = (linkNamePageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === pageSpecPageType || item.value === 'PAGE_TYPE_WECHAT_OFFICIAL_ACCOUNT_DETAIL').map(item => ({ label: item.description, value: item.value }))
  135. }
  136. if (putInType === 'GAME') {
  137. let jumpInfoPageType = creativeComponents?.jump_info?.children?.page_type?.enumProperty?.enumeration
  138. if (jumpInfoPageType?.length && jumpInfoPageType?.length !== linkNamePageType?.enumProperty?.enumeration?.length) {
  139. return
  140. }
  141. }
  142. return <Form.Item style={{ marginBottom: 0 }}>
  143. <div className={style.newSpace}>
  144. <div className={style.newSpace_top}>
  145. <div className={style.newSpace_title}>朋友圈文字链</div>
  146. <Form.Item name={'textLinkShow'} valuePropName="checked" style={{ marginBottom: 0 }}>
  147. <Switch disabled={textLinkRequired} onChange={() => setIsUpdate(true)} />
  148. </Form.Item>
  149. </div>
  150. </div>
  151. {textLinkShow && <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  152. <div className={style.newSpace}>
  153. <div className={style.newSpace_top}>
  154. <div className={style.newSpace_title}>文字链文案</div>
  155. <Form.Item
  156. name={['textLink', 'value', 'linkNameType']}
  157. rules={[{ required: true, message: '请选择文字链文案' }]}
  158. style={{ marginBottom: 0 }}
  159. >
  160. <Select
  161. style={{ width: 480 }}
  162. showSearch
  163. placeholder="请选择文字链文案"
  164. optionFilterProp="children"
  165. filterOption={(input, option: any) =>
  166. (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
  167. }
  168. onChange={() => setIsUpdate(true)}
  169. options={linkNameEnumeration}
  170. />
  171. </Form.Item>
  172. </div>
  173. </div>
  174. {linkNamePageType && <div className={style.newSpace}>
  175. <div className={style.newSpace_top}>
  176. <div className={style.newSpace_title}>跳转落地页</div>
  177. <Form.Item
  178. name={['textLink', 'value', 'jumpInfo', 'pageType']}
  179. rules={[{ required: true, message: '请选择跳转落地页类型' }]}
  180. style={{ marginBottom: 0 }}
  181. >
  182. <New1Radio data={linkNamePageTypeEnumeration} />
  183. </Form.Item>
  184. </div>
  185. </div>}
  186. </Card>}
  187. </Form.Item>
  188. }
  189. return null
  190. }, [creativeComponents?.text_link, pageSpec, textLinkShow, creativeTemplateId, automaticSiteEnabled, putInType])
  191. /** 行动按钮 */
  192. const actionButtonContent = useMemo(() => {
  193. // 卡片广告用卡片组件控制
  194. if ([1707, 1708].includes(creativeTemplateId) && !cardType?.includes('action_button')) {
  195. return null
  196. }
  197. let actionButton = creativeComponents?.action_button
  198. if (actionButton) {
  199. let pageSpecPageType = pageSpec?.[0]?.pageType || 'PAGE_TYPE_WECHAT_CANVAS'
  200. let actionButtonRequired, butttonTextEnumeration: { label: string, value: string }[] = [];
  201. let actionButtonPageType, actionButtonPageTypeEnumeration: PULLIN.DataType[] = [];
  202. actionButtonRequired = actionButton.required
  203. let buttonText = actionButton.children.button_text
  204. butttonTextEnumeration = (buttonText.enumProperty.enumeration as { value: string }[]).map(item => ({ label: item.value, value: item.value }))
  205. actionButtonPageType = actionButton?.children?.page_type
  206. if (actionButtonPageType) {
  207. actionButtonPageTypeEnumeration = (actionButtonPageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === pageSpecPageType).map(item => ({ label: item.description, value: item.value }))
  208. if (pageSpecPageType === 'PAGE_TYPE_OFFICIAL' && actionButtonPageTypeEnumeration.length === 0) {
  209. actionButtonPageTypeEnumeration = (actionButtonPageType?.enumProperty?.enumeration as { value: string, description: string }[]).filter(item => item.value === 'PAGE_TYPE_WECHAT_FOCUS_DAILOG').map(item => ({ label: item.description, value: item.value }))
  210. }
  211. }
  212. return <Form.Item style={{ marginBottom: 0 }}>
  213. {![1707, 1708].includes(creativeTemplateId) && <div className={style.newSpace}>
  214. <div className={style.newSpace_top}>
  215. <div className={style.newSpace_title}>行动按钮</div>
  216. <Form.Item name={'actionButtonShow'} valuePropName="checked" style={{ marginBottom: 0 }}>
  217. <Switch disabled={actionButtonRequired} />
  218. </Form.Item>
  219. </div>
  220. </div>}
  221. {(actionButtonShow || cardType?.includes('action_button')) && <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  222. <div className={style.newSpace}>
  223. <div className={style.newSpace_top}>
  224. <div className={style.newSpace_title}>按钮文案</div>
  225. <Form.Item
  226. name={['actionButton', 'value', 'buttonText']}
  227. rules={[{ required: true, message: '请选择按钮文案' }]}
  228. style={{ marginBottom: 0 }}
  229. >
  230. <Select
  231. style={{ width: 480 }}
  232. showSearch
  233. placeholder="请选择按钮文案"
  234. optionFilterProp="children"
  235. filterOption={(input, option: any) =>
  236. (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
  237. }
  238. options={butttonTextEnumeration}
  239. />
  240. </Form.Item>
  241. </div>
  242. </div>
  243. {actionButtonPageType && <div className={style.newSpace}>
  244. <div className={style.newSpace_top}>
  245. <div className={style.newSpace_title}>跳转落地页</div>
  246. <Form.Item
  247. name={['actionButton', 'value', 'jumpInfo', 'pageType']}
  248. rules={[{ required: true, message: '请选择跳转落地页类型' }]}
  249. style={{ marginBottom: 0 }}
  250. >
  251. <New1Radio data={actionButtonPageTypeEnumeration} />
  252. </Form.Item>
  253. </div>
  254. </div>}
  255. </Card>}
  256. </Form.Item>
  257. }
  258. return null
  259. }, [creativeComponents?.action_button, pageSpec, actionButtonShow, cardType, creativeTemplateId])
  260. /** 标签 */
  261. const labelContent = useMemo(() => {
  262. // 卡片广告用卡片组件控制
  263. if ([1707, 1708].includes(creativeTemplateId) && !cardType?.includes('label')) {
  264. return null
  265. }
  266. let label = creativeComponents?.label;
  267. if (label) {
  268. const list = label.children.list
  269. return <Form.Item name={'creativeLabelDTOS'} style={{ marginTop: 16, marginBottom: 0 }} label={<strong>&nbsp;&nbsp;&nbsp;标签{label.required ? '' : '(选填)'}</strong>}>
  270. <SelectLabel arrayProperty={list.arrayProperty} putInType={putInType} />
  271. </Form.Item>
  272. }
  273. return null
  274. }, [creativeComponents?.label, cardType, putInType])
  275. /** 弹幕 */
  276. const barrageContent = useMemo(() => {
  277. let barrage = creativeComponents?.barrage;
  278. if (barrage) {
  279. return <Form.Item
  280. name={'creativeBarrageDTOS'}
  281. style={{ marginTop: 16, marginBottom: 0 }}
  282. label={<strong>&nbsp;&nbsp;&nbsp;弹幕{barrage.required ? '' : '(选填)'}</strong>}
  283. rules={[
  284. {
  285. required: barrage.required, validator(_, value) {
  286. if (value && (value?.length < 3 || value?.length > 20)) {
  287. return Promise.reject('弹幕数量须为3-20条')
  288. }
  289. return Promise.resolve()
  290. }
  291. }
  292. ]}
  293. >
  294. <SelectBarrage arrayProperty={{ minNumber: 0, maxNumber: 20 }} putInType={putInType} />
  295. </Form.Item>
  296. }
  297. return null
  298. }, [creativeComponents?.barrage, putInType])
  299. /** 数据外显 */
  300. const showDataContent = useMemo(() => {
  301. let showData = creativeComponents?.show_data;
  302. if (showData) {
  303. let conversionDataTypeEnumeration: PULLIN.DataType[] = [];
  304. let conversionTargetTypeEnumeration: PULLIN.DataType[] = [];
  305. let link_name_type = linkNameType || ''
  306. let showDataRequired = showData?.required
  307. let conversionDataType = showData?.children?.conversion_data_type
  308. if (conversionDataType) {
  309. conversionDataTypeEnumeration = (conversionDataType.enumProperty.enumeration as { value: string, category: string, description: string }[])
  310. .filter(item => item.category === link_name_type)
  311. .map(item => ({ label: item.description, value: item.value }))
  312. if (conversionDataTypeEnumeration.length === 0) {
  313. conversionDataTypeEnumeration = (conversionDataType.enumProperty.enumeration as { value: string, category: string, description: string }[])
  314. .filter(item => item.category === '')
  315. .map(item => ({ label: item.description, value: item.value }))
  316. }
  317. }
  318. let conversionTargetType = showData?.children?.conversion_target_type
  319. if (conversionTargetType) {
  320. conversionTargetTypeEnumeration = (conversionTargetType.enumProperty.enumeration as { value: string, category: string, description: string }[])
  321. .filter(item => item.category === link_name_type)
  322. .map(item => ({ label: item.description, value: item.value }))
  323. if (conversionTargetTypeEnumeration.length === 0) {
  324. conversionTargetTypeEnumeration = (conversionTargetType.enumProperty.enumeration as { value: string, category: string, description: string }[])
  325. .filter(item => item.category === '')
  326. .map(item => ({ label: item.description, value: item.value }))
  327. }
  328. }
  329. // 卡片广告去掉商品数据
  330. if ([1707, 1708].includes(creativeTemplateId)) {
  331. conversionDataTypeEnumeration = conversionDataTypeEnumeration.filter(item => item.value !== "CONVERSION_DATA_PRODUCT_DATA")
  332. }
  333. return <Form.Item style={{ marginBottom: 0 }}>
  334. <div className={style.newSpace}>
  335. <div className={style.newSpace_top}>
  336. <div className={style.newSpace_title}>数据外显</div>
  337. <Form.Item name={'showDataShow'} valuePropName="checked" style={{ marginBottom: 0 }}>
  338. <Switch disabled={showDataRequired} onChange={() => setIsUpdate(true)} />
  339. </Form.Item>
  340. </div>
  341. </div>
  342. {showDataShow && <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  343. <div className={style.newSpace}>
  344. <div className={style.newSpace_top}>
  345. <div className={style.newSpace_title}>数据类型</div>
  346. <Form.Item
  347. name={['showData', 'value', 'conversionDataType']}
  348. rules={[{ required: true, message: '请选择数据类型' }]}
  349. style={{ marginBottom: 0 }}
  350. >
  351. <Radio.Group options={conversionDataTypeEnumeration} />
  352. </Form.Item>
  353. </div>
  354. </div>
  355. <div className={style.newSpace}>
  356. <div className={style.newSpace_top}>
  357. <div className={style.newSpace_title}>转化行为</div>
  358. <Form.Item
  359. name={['showData', 'value', 'conversionTargetType']}
  360. rules={[{ required: true, message: '请选择转化行为' }]}
  361. style={{ marginBottom: 0 }}
  362. >
  363. <Radio.Group options={conversionTargetTypeEnumeration} />
  364. </Form.Item>
  365. </div>
  366. </div>
  367. </Card>}
  368. </Form.Item>
  369. }
  370. return null
  371. }, [creativeComponents?.show_data, linkNameType, showDataShow, creativeTemplateId])
  372. /** 浮层卡片 */
  373. const floatingZoneContent = useMemo(() => {
  374. let floatingZone = creativeComponents?.floating_zone;
  375. if (floatingZone) {
  376. let required = floatingZone?.required
  377. let children = floatingZone.children
  378. const { floating_zone_type, floating_zone_image_id, floating_zone_single_image_id, floating_zone_name, floating_zone_desc, floating_zone_button_text } = children
  379. const floatingZoneTypeList: PULLIN.DataType[] = []
  380. if (floating_zone_image_id) {
  381. floatingZoneTypeList.push({ label: "图文复合类型", value: "FLOATING_ZONE_TYPE_IMAGE_TEXT" })
  382. }
  383. if (floating_zone_single_image_id) {
  384. floatingZoneTypeList.push({ label: "单图类型", value: "FLOATING_ZONE_TYPE_SINGLE_IMAGE" })
  385. }
  386. let floatingZoneButtonTextEnumeration: { label: string, value: string }[] = [];
  387. if (floating_zone_button_text) {
  388. floatingZoneButtonTextEnumeration = (floating_zone_button_text.enumProperty.enumeration as { value: string, description: string }[]).map(item => ({ label: item.value, value: item.value }))
  389. }
  390. return <>
  391. <Form.Item style={{ marginBottom: 0 }}>
  392. <div className={style.newSpace} style={{ border: 'none' }}>
  393. <div className={style.newSpace_top}>
  394. <div className={style.newSpace_title}>浮层卡片</div>
  395. <Form.Item name={['floatingZone', 'value', 'floatingZoneSwitch']} rules={[{ required, message: '请选择浮层卡片!' }]} valuePropName="checked" style={{ marginBottom: 0 }}>
  396. <Switch
  397. disabled={required}
  398. onChange={(e) => {
  399. if (e) {
  400. let floatingZoneData: any = {
  401. value: {
  402. floatingZoneSwitch: true,
  403. floatingZoneType: 'FLOATING_ZONE_TYPE_IMAGE_TEXT',
  404. floatingZoneButtonText: '了解更多'
  405. }
  406. }
  407. if (floating_zone_image_id && floating_zone_button_text) {
  408. floatingZoneData.value.floatingZoneButtonText = floatingZone.children.floating_zone_button_text?.enumProperty?.enumeration[0]?.value
  409. }
  410. form.setFieldsValue({ floatingZone: floatingZoneData })
  411. }
  412. }}
  413. />
  414. </Form.Item>
  415. </div>
  416. {floatingZoneSwitch && <Form.Item name={['floatingZone', 'value', 'floatingZoneType']} rules={[{ required: floating_zone_type?.required, message: `请选择` + floating_zone_type?.description }]} style={{ marginTop: 10, marginBottom: 0 }}>
  417. <New1Radio data={floatingZoneTypeList} />
  418. </Form.Item>}
  419. </div>
  420. {(floatingZoneSwitch && floatingZoneType === 'FLOATING_ZONE_TYPE_IMAGE_TEXT') ? <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  421. <div className={style.newSpace}>
  422. <div className={style.newSpace_top} style={{ alignItems: 'flex-start' }}>
  423. <div className={style.newSpace_title}>{floating_zone_image_id?.description}</div>
  424. <Form.Item
  425. rules={[{ required: true, message: '请选择素材!' }]}
  426. name={['floatingZone', 'value', 'image']}
  427. >
  428. <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
  429. setSelectCloudData({ defaultParams: { sizeQueries: [{ relation: '=', width: floating_zone_image_id.restriction.imageRestriction.width, height: floating_zone_image_id.restriction.imageRestriction.height }], fileSize: floating_zone_image_id.restriction.imageRestriction.fileSize * 1024, materialType: 'image' }, num: 1 })
  430. setMaterialConfig({
  431. ...materialConfig,
  432. type: 'floatingZoneImage',
  433. max: 1,
  434. index: 1,
  435. adcreativeTemplateId: creativeTemplateId
  436. })
  437. setTimeout(() => {
  438. setSelectVideoVisible(true)
  439. }, 100)
  440. }}>
  441. <p>
  442. {image?.floatingZoneImageUrl ? <img src={image?.floatingZoneImageUrl} /> : <>
  443. <span>{`推荐尺寸(${floating_zone_image_id.restriction.imageRestriction.width} x ${floating_zone_image_id.restriction.imageRestriction.height})`}</span>
  444. <span>{`${floating_zone_image_id.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${floating_zone_image_id.restriction.imageRestriction.fileSize}KB`}</span>
  445. </>}
  446. </p>
  447. </div>
  448. </Form.Item>
  449. </div>
  450. </div>
  451. <div className={style.newSpace}>
  452. <div className={style.newSpace_top}>
  453. <div className={style.newSpace_title}>{floating_zone_name?.description}</div>
  454. <Form.Item
  455. name={['floatingZone', 'value', 'floatingZoneName']}
  456. rules={[{
  457. required: floating_zone_name.required, message: '请输入正确的' + floating_zone_name.description, validator: (rule, value) => {
  458. if (!value) {
  459. return Promise.reject()
  460. } else if (!value.match(RegExp(floating_zone_name.restriction.textRestriction.textPattern))) {
  461. return Promise.reject()
  462. } else if (txtLength(value) > floating_zone_name.restriction.textRestriction.maxLength) {
  463. return Promise.reject()
  464. }
  465. return Promise.resolve()
  466. }
  467. }]}
  468. style={{ marginBottom: 0 }}
  469. >
  470. <TextAideInput isShowAjax={false} isSelectEmoji={false} placeholder={'请输入' + floating_zone_name.description} style={{ width: 480 }} maxTextLength={floating_zone_name.restriction.textRestriction.maxLength} />
  471. </Form.Item>
  472. </div>
  473. </div>
  474. <div className={style.newSpace}>
  475. <div className={style.newSpace_top}>
  476. <div className={style.newSpace_title}>{floating_zone_desc?.description}</div>
  477. <Form.Item
  478. name={['floatingZone', 'value', 'floatingZoneDesc']}
  479. rules={[{
  480. required: floating_zone_desc.required, message: '请输入正确的' + floating_zone_desc.description, validator: (rule, value) => {
  481. if (!value) {
  482. return Promise.reject()
  483. } else if (!value.match(RegExp(floating_zone_desc.restriction.textRestriction.textPattern))) {
  484. return Promise.reject()
  485. } else if (txtLength(value) > floating_zone_desc.restriction.textRestriction.maxLength) {
  486. return Promise.reject()
  487. }
  488. return Promise.resolve()
  489. }
  490. }]}
  491. style={{ marginBottom: 0 }}
  492. >
  493. <TextAideInput isShowAjax={false} isSelectEmoji={false} placeholder={'请输入' + floating_zone_desc.description} style={{ width: 480 }} maxTextLength={floating_zone_desc.restriction.textRestriction.maxLength} />
  494. </Form.Item>
  495. </div>
  496. </div>
  497. <div className={style.newSpace}>
  498. <div className={style.newSpace_top}>
  499. <div className={style.newSpace_title}>按钮文案</div>
  500. <Form.Item
  501. name={['floatingZone', 'value', 'floatingZoneButtonText']}
  502. rules={[{ required: true, message: '请选择按钮文案' }]}
  503. style={{ marginBottom: 0 }}
  504. >
  505. <Select
  506. style={{ width: 480 }}
  507. showSearch
  508. placeholder="请选择按钮文案"
  509. optionFilterProp="children"
  510. filterOption={(input, option: any) =>
  511. (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
  512. }
  513. options={floatingZoneButtonTextEnumeration}
  514. />
  515. </Form.Item>
  516. </div>
  517. </div>
  518. </Card> : (floatingZoneSwitch && floatingZoneType === 'FLOATING_ZONE_TYPE_SINGLE_IMAGE') ? <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  519. <div className={style.newSpace}>
  520. <div className={style.newSpace_top} style={{ alignItems: 'flex-start' }}>
  521. <div className={style.newSpace_title}>{floating_zone_single_image_id?.description}</div>
  522. <Form.Item
  523. rules={[{ required: true, message: '请选择素材!' }]}
  524. name={['floatingZone', 'value', 'image']}
  525. >
  526. <div className={`${styles.box} ${styles.image}`} style={{ width: 300, height: 160 }} onClick={() => {
  527. setSelectCloudData({ defaultParams: { sizeQueries: [{ relation: '=', width: floating_zone_single_image_id.restriction.imageRestriction.width, height: floating_zone_single_image_id.restriction.imageRestriction.height }], fileSize: floating_zone_single_image_id.restriction.imageRestriction.fileSize * 1024, materialType: 'image' }, num: 1 })
  528. setMaterialConfig({
  529. ...materialConfig,
  530. type: 'floatingZoneSingleImage',
  531. max: 1,
  532. index: 1,
  533. adcreativeTemplateId: creativeTemplateId
  534. })
  535. setTimeout(() => {
  536. setSelectVideoVisible(true)
  537. }, 100)
  538. }}>
  539. <p>
  540. {image?.floatingZoneSingleImageUrl ? <img src={image?.floatingZoneSingleImageUrl} /> : <>
  541. <span>{`推荐尺寸(${floating_zone_single_image_id.restriction.imageRestriction.width} x ${floating_zone_single_image_id.restriction.imageRestriction.height})`}</span>
  542. <span>{`${floating_zone_single_image_id.restriction.imageRestriction.fileFormat?.map((str: any) => str?.replace('IMAGE_TYPE_', ''))};小于 ${floating_zone_single_image_id.restriction.imageRestriction.fileSize}KB`}</span>
  543. </>}
  544. </p>
  545. </div>
  546. </Form.Item>
  547. </div>
  548. </div>
  549. </Card> : null}
  550. </Form.Item>
  551. </>
  552. }
  553. return null
  554. }, [creativeComponents?.floating_zone, floatingZoneSwitch, floatingZoneType, image])
  555. useEffect(() => {
  556. let data = [
  557. { label: '不使用', value: 'not', disabled: false },
  558. { label: '行动按钮', value: 'action_button', disabled: cardType?.some(item => ['chosen_button', 'shop_imag'].includes(item)) },
  559. // { label: '选择按钮', value: 'chosen_button', disabled: cardType?.some(item => ['label', 'action_button', 'shop_imag'].includes(item)) },
  560. // { label: '卖点图', value: 'shop_imag', disabled: cardType?.some(item => ['chosen_button'].includes(item)) },
  561. { label: '标签', value: 'label', disabled: cardType?.some(item => ['chosen_button', 'count_down'].includes(item)) },
  562. // { label: '倒计时', value: 'count_down', disabled: cardType?.some(item => ['living_desc', 'label'].includes(item)) },
  563. // { label: '轮播文案', value: 'living_desc', disabled: cardType?.some(item => ['count_down'].includes(item)) }
  564. ]
  565. if (cardType?.length >= 3) {
  566. setCardData(data.map(item => {
  567. if (cardType.includes(item.value)) {
  568. return { ...item, disabled: false }
  569. } else if (item.value === 'not') {
  570. return { ...item, disabled: false }
  571. } else {
  572. return { ...item, disabled: true }
  573. }
  574. }))
  575. } else {
  576. setCardData(data)
  577. }
  578. }, [cardType])
  579. if (Object.keys(creativeComponents).some(key => ['text_link', 'action_button', 'show_data', 'floating_zone', 'label', 'barrage'].includes(key))) {
  580. return <Card
  581. title={<strong style={{ fontSize: 18 }}>营销组件</strong>}
  582. className="cardResetCss"
  583. >
  584. {/* 卡片广告所需 */}
  585. {[1707, 1708].includes(creativeTemplateId) && <Form.Item style={{ marginBottom: 0 }}>
  586. <div className={style.newSpace}>
  587. <div className={style.newSpace_top}>
  588. <div className={style.newSpace_title}>
  589. <Space>
  590. <strong>卡片组件</strong>
  591. <Tooltip title={<div>
  592. 卡片组件有以下使用条件:<br />
  593. 1.卡片组件最多只能使用 3 个<br />
  594. 2.倒计时不能与标签、轮播文案同时使用<br />
  595. 3.卖点图必须与行动按钮同时使用<br />
  596. 4.选择按钮不能与行动按钮、标签、卖点图同时使用
  597. </div>}>
  598. <QuestionCircleFilled />
  599. </Tooltip>
  600. </Space>
  601. </div>
  602. <Form.Item
  603. noStyle
  604. name={'cardType'}
  605. rules={[{ required: true, message: '请选择跳转落地页类型' }]}
  606. style={{ marginBottom: 0 }}
  607. getValueFromEvent={(e: string[]) => {
  608. if (e.length > 1 && !cardType.includes('not') && e.includes('not')) {
  609. return ['not'];
  610. }
  611. if (e.includes('shop_imag') && !e.includes('action_button')) {
  612. e.push('action_button')
  613. }
  614. return e.length > 0 ? (e.length > 1 && e.includes('not') ? e.filter(item => item !== 'not') : e) : ['not'];
  615. }}
  616. >
  617. <Checkbox.Group options={cardData} onChange={(e) => {
  618. console.log(e)
  619. }} />
  620. </Form.Item>
  621. </div>
  622. </div>
  623. </Form.Item>}
  624. {textLinkContent}
  625. {actionButtonContent}
  626. {labelContent}
  627. {barrageContent}
  628. {showDataContent}
  629. {floatingZoneContent}
  630. {/* 选择视频素材 */}
  631. {(selectVideoVisible && selectCloudData) && <SelectCloudNew
  632. {...selectCloudData}
  633. visible={selectVideoVisible}
  634. onClose={() => {
  635. setSelectVideoVisible(false)
  636. setSelectCloudData(undefined)
  637. }}
  638. sliderImgContent={materialConfig.type === 'floatingZoneSingleImage' ? image?.floatingZoneSingleImageUrl ? [{ oss_url: image.floatingZoneSingleImageUrl, id: image.floatingZoneSingleImageId }] : undefined : image?.floatingZoneImageUrl ? [{ oss_url: image.floatingZoneImageUrl, id: image.floatingZoneImageId }] : undefined}
  639. onChange={(content: any) => {
  640. if (content.length > 0) {
  641. if (materialConfig.type === 'floatingZoneSingleImage') {
  642. form.setFieldsValue({
  643. floatingZone: {
  644. ...floatingZone,
  645. value: {
  646. ...floatingZone?.value,
  647. image: { floatingZoneSingleImageId: content[0]?.id, floatingZoneSingleImageUrl: content[0]?.oss_url, floatingZoneSingleImageMaterialType: 0 }
  648. }
  649. }
  650. })
  651. } else {
  652. form.setFieldsValue({
  653. floatingZone: {
  654. ...floatingZone,
  655. value: {
  656. ...floatingZone?.value,
  657. image: { floatingZoneImageId: content[0]?.id, floatingZoneImageUrl: content[0]?.oss_url, floatingZoneImageMaterialType: 0 }
  658. }
  659. }
  660. })
  661. }
  662. }
  663. setSelectVideoVisible(false)
  664. }}
  665. />}
  666. </Card>
  667. } else {
  668. return null
  669. }
  670. }
  671. export default React.memo(CreativeConversionAssistant)