creativeConversionAssistant.tsx 39 KB

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