index.tsx 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. import { useAjax } from "@/Hook/useAjax"
  2. import { CheckOutlined, CloseOutlined, QuestionCircleOutlined, SyncOutlined } from "@ant-design/icons"
  3. import { Button, Checkbox, Input, message, Modal, Select, Space, Table, Tooltip, Typography } from "antd"
  4. import React, { useEffect, useState } from "react"
  5. import style from '../GoodsModal/index.less'
  6. import columns from "./tableConfig"
  7. import { getAdqLandingPageListApi, getWXDownPageAuthInfoListApi } from "@/services/adqV3/global"
  8. import { OPTION_ENUM } from "../../tencentAdPutIn/const"
  9. import New1Radio from "../New1Radio"
  10. const { Title, Text } = Typography;
  11. /**
  12. * 获取adq落地页
  13. * @returns
  14. */
  15. interface Props {
  16. visible?: boolean,
  17. onClose?: () => void,
  18. onChange?: (value: { data: PULLIN.AccountCreateLogsProps[], landingPageType: 0 | 1 }) => void,
  19. adgroups: any
  20. dynamic: any
  21. data: PULLIN.AccountCreateLogsProps[]
  22. }
  23. const PageModal: React.FC<Props> = (props) => {
  24. /*************************/
  25. const { visible, onClose, data: data1, adgroups, onChange, dynamic } = props
  26. const { marketingGoal, marketingAssetOuterSpec: { marketingTargetType }, marketingCarrierType, siteSet } = adgroups
  27. const { creativeTemplateId, deliveryMode, creativeComponents: { mainJumpInfo }, landingPageType } = dynamic
  28. let overrideCanvasHeadOption = ''
  29. if (mainJumpInfo && mainJumpInfo?.length > 0) {
  30. mainJumpInfo.forEach((item: any) => {
  31. let { pageSpec } = item.value
  32. let key = Object.keys(pageSpec)[0]
  33. let pageSpecValue = pageSpec[key]
  34. overrideCanvasHeadOption = pageSpecValue.overrideCanvasHeadOption
  35. })
  36. }
  37. const [selectAdz, setSelectAdz] = useState<number>(1) // 选择广告主
  38. const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1 || [])
  39. const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean, isCanvasType?: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
  40. const [loading, setLoading] = useState<boolean>(false)
  41. const [wXDownPageAuthInfo, setWXDownPageAuthInfo] = useState<any>({})
  42. const [tableData, setTableData] = useState<any>({})
  43. const [pageAllocationType, setPageAllocationType] = useState<0 | 1>(landingPageType || 0)
  44. const listAjax = useAjax((params) => getAdqLandingPageListApi(params))
  45. /*************************/
  46. useEffect(() => {
  47. if (data?.length > 0) {
  48. setQueryForm({ ...queryForm, pageNum: 1, accountId: data[selectAdz - 1].accountId })
  49. }
  50. }, [])
  51. useEffect(() => {
  52. if (data?.length > 0) {
  53. setLoading(true)
  54. let accountDataList = data.map(item => item.accountId)
  55. let infoAjax = accountDataList.map(accountId => getWXDownPageAuthInfoListApi(accountId))
  56. let newData: any = {}
  57. Promise.all(infoAjax).then(res => {
  58. res?.forEach((item, index) => {
  59. newData[accountDataList[index]] = item?.data || []
  60. })
  61. setWXDownPageAuthInfo(newData)
  62. setLoading(false)
  63. }).catch(() => setLoading(false))
  64. }
  65. }, [])
  66. useEffect(() => {
  67. if (queryForm?.accountId) {
  68. getList()
  69. }
  70. }, [queryForm, marketingGoal, marketingTargetType, marketingCarrierType, siteSet, mainJumpInfo])
  71. // 获取落地页列表
  72. const getList = () => {
  73. let params: any = { ...queryForm, pageStatus: 'NORMAL', marketingGoal, marketingTargetType, marketingCarrierType, siteSet }
  74. if (params.isSqDownPage) {
  75. if (!params?.ownerUid) {
  76. setTableData({ total: 0, records: [] })
  77. return
  78. }
  79. } else {
  80. delete params?.ownerUid
  81. }
  82. if (params?.isCanvasType) {
  83. params.canvasType = 'CANVAS_TYPE_COMMON_PAGE'
  84. }
  85. delete params?.isCanvasType
  86. delete params.isSqDownPage
  87. listAjax.run(params).then(res => {
  88. setTableData(res || {})
  89. })
  90. }
  91. const handleOk = () => {
  92. if (deliveryMode === "DELIVERY_MODE_COMPONENT") {
  93. if (creativeTemplateId === 910) {
  94. if (!data?.every(item => {
  95. return item.pageList?.length >= mainJumpInfo?.length && item.pageList?.length % mainJumpInfo?.length === 0
  96. })) {
  97. message.error(`当前落地页数量不足,跳转类型选择了${mainJumpInfo.length}组,落地页应当选择大于等于${mainJumpInfo.length},并且是${mainJumpInfo?.length}倍数`)
  98. return
  99. }
  100. } else {
  101. if (!data?.every(item => item.pageList?.length === mainJumpInfo?.length)) {
  102. message.error(`当前落地页数量不足,跳转类型选择了${mainJumpInfo.length}组,落地页应当选择${mainJumpInfo.length}组`)
  103. return
  104. }
  105. }
  106. }
  107. if (data?.every(item => item.pageList)) {
  108. onChange && onChange({ data, landingPageType: pageAllocationType })
  109. } else {
  110. message.error('还有账号未选择落地页!')
  111. }
  112. }
  113. /** 设置选中广告主 */
  114. const handleSelectAdz = (value: number, item: any) => {
  115. if (value === selectAdz) {
  116. return
  117. }
  118. let accountId = data[value - 1].accountId
  119. setQueryForm({ ...queryForm, pageNum: 1, accountId, ownerUid: wXDownPageAuthInfo[accountId]?.[0]?.accountId })
  120. setSelectAdz(value)
  121. }
  122. /** 一键设置 */
  123. const setOnekey = () => {
  124. let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
  125. function setPageData(pageIndex: number) {
  126. let pageList = data[selectAdz - 1]['pageList']
  127. let count = pageList.length
  128. let curPageData = pageList[pageIndex]
  129. let pageName: string = curPageData.pageName
  130. const hide = message.loading(`正在查找设置《${pageName}》当前第${pageIndex + 1}个...`, 0, () => {
  131. message.success('设置成功');
  132. });
  133. let filterData = newData?.filter(item => item.accountId !== data[selectAdz - 1].accountId)
  134. let ajax = filterData?.map(item => {
  135. let params: any = { ...queryForm, pageNum: 1, accountId: item.accountId, ownerUid: wXDownPageAuthInfo[item.accountId]?.[0]?.accountId, pageNameFull: pageName, pageStatus: 'NORMAL', marketingGoal, marketingTargetType, marketingCarrierType, siteSet }
  136. if (!params.isSqDownPage) {
  137. delete params?.ownerUid
  138. }
  139. if (params?.isCanvasType) {
  140. params.canvasType = 'CANVAS_TYPE_COMMON_PAGE'
  141. }
  142. delete params?.isCanvasType
  143. delete params.isSqDownPage
  144. return getAdqLandingPageListApi(params)
  145. })
  146. Promise.all(ajax).then(res => {
  147. if (res && Array.isArray(res)) {
  148. res.forEach((item: any, index) => {
  149. let records = item?.data?.records
  150. if (Array.isArray(records) && records?.length > 0) {
  151. let record = records.find(item => item.pageName === pageName)
  152. let filterD = filterData[index]
  153. newData = newData.map(item => {
  154. if (item.accountId.toString() === filterD.accountId.toString() && record) {
  155. if (pageIndex === 0) {
  156. return { ...item, pageList: [{ ...record, id: record.pageId }] }
  157. } else {
  158. let pageList = item.pageList || []
  159. pageList.push({ ...record, id: record.pageId })
  160. return { ...item, pageList }
  161. }
  162. }
  163. return item
  164. })
  165. }
  166. })
  167. if (count > pageIndex + 1) {
  168. setPageData(pageIndex + 1)
  169. }
  170. if (count === pageIndex + 1) {
  171. setData(newData)
  172. }
  173. }
  174. message.success('设置完成');
  175. hide()
  176. })
  177. }
  178. setPageData(0)
  179. }
  180. return <Modal
  181. title={<Space>
  182. <strong>选择落地页</strong>
  183. {data?.length > 1 && <Button style={{ padding: 0, margin: 0 }} disabled={!data[selectAdz - 1]['pageList']?.length} onClick={setOnekey} type="link" loading={listAjax.loading}>
  184. <Space>
  185. <span style={{ fontSize: 12 }}>一键设置</span>
  186. <Tooltip color="#FFF" overlayInnerStyle={{ color: '#000' }} title="设置其它账号有相同名称的落地页为那个账号的落地页(需要落地页名称相同,否则不设置,注意只会根据当前条件去搜索,比如选择了被授权落地页,只会去被授权落地页里找对应落地页)">
  187. <QuestionCircleOutlined />
  188. </Tooltip>
  189. </Space>
  190. </Button>}
  191. <Button
  192. type="link"
  193. danger
  194. disabled={!data?.some(item => item.pageList?.length)}
  195. onClick={() => {
  196. setData(data => data.map(item => ({ ...item, pageList: [] })))
  197. }}
  198. >全部清空</Button>
  199. {![910].includes(creativeTemplateId) && <Text type="warning">{OPTION_ENUM[overrideCanvasHeadOption as keyof typeof OPTION_ENUM]}</Text>}
  200. </Space>}
  201. open={visible}
  202. onCancel={() => { onClose && onClose() }}
  203. onOk={handleOk}
  204. width={1100}
  205. className={`${style.SelectPackage} modalResetCss`}
  206. bodyStyle={{ padding: '0 10px 0 10px' }}
  207. >
  208. {[910].includes(creativeTemplateId) && <div className={style.pageType}>
  209. <Space size={12}>
  210. <strong>落地页分配规则</strong>
  211. <New1Radio
  212. data={[{ label: '全部相同', value: 0 }, { label: '平均分配', value: 1 }]}
  213. value={pageAllocationType}
  214. onChange={(e) => {
  215. setPageAllocationType(e)
  216. }}
  217. />
  218. </Space>
  219. </div>}
  220. <div className={style.content}>
  221. <div className={style.left}>
  222. <h4 className={style.title}>媒体账户</h4>
  223. <div className={style.accountIdList}>
  224. {data?.map((item, index) => (
  225. <div key={index} onClick={() => { if (!loading) handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
  226. {item?.accountId}
  227. {data[index].pageList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
  228. </div>
  229. ))}
  230. </div>
  231. </div>
  232. <div className={style.right}>
  233. <Space style={{ marginBottom: 10 }}>
  234. <Checkbox
  235. onChange={(e) => {
  236. setQueryForm({ ...queryForm, pageNum: 1, isCanvasType: e.target.checked })
  237. }}
  238. checked={queryForm.isCanvasType}
  239. ><span style={{ fontSize: 12 }}>展示外换内组件原生页</span></Checkbox>
  240. <Checkbox
  241. onChange={(e) => {
  242. if (e.target.checked && wXDownPageAuthInfo && Object.keys(wXDownPageAuthInfo).length) {
  243. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: wXDownPageAuthInfo[data[selectAdz - 1].accountId]?.[0]?.accountId, isSqDownPage: true })
  244. } else {
  245. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: undefined, isSqDownPage: false })
  246. }
  247. }}
  248. checked={queryForm.isSqDownPage}
  249. ><span style={{ fontSize: 12 }}>被授权落地页</span></Checkbox>
  250. {queryForm.isSqDownPage && <Select
  251. placeholder='选择原生页授权方信息'
  252. style={{ width: 210 }}
  253. showSearch
  254. filterOption={(input: any, option: any) =>
  255. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  256. }
  257. allowClear
  258. onChange={(value) => {
  259. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: value })
  260. }}
  261. value={queryForm?.ownerUid}
  262. dropdownMatchSelectWidth={false}
  263. >
  264. {wXDownPageAuthInfo?.[data[selectAdz - 1].accountId]?.map((item: { accountId: number; accountName: string }) => {
  265. return <Select.Option value={item.accountId} key={item.accountId}>{item.accountName}({item.accountId})</Select.Option>
  266. })}
  267. </Select>}
  268. <Input value={queryForm?.pageName} style={{ width: 150 }} allowClear placeholder='请输入落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
  269. <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
  270. </Space>
  271. <Table
  272. columns={columns()}
  273. dataSource={tableData?.records}
  274. size="small"
  275. loading={listAjax?.loading || loading}
  276. scroll={{ y: 400 }}
  277. bordered
  278. rowKey={'pageId'}
  279. pagination={{
  280. total: tableData?.total,
  281. defaultPageSize: 20,
  282. current: tableData?.current,
  283. pageSize: tableData?.size
  284. }}
  285. onChange={(pagination) => {
  286. const { current, pageSize } = pagination
  287. setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
  288. }}
  289. /**
  290. * canvasType
  291. * VIDEO
  292. * VIDEO_1280_960 横板1280*960
  293. * VIDEO_1280_720 横板1280*720
  294. * VERTICAL_VIDEO_NEW_916 9:16 视频
  295. * COMMON_PAGE 引用外层素材原生页
  296. * IMAGE
  297. */
  298. rowSelection={{
  299. type: ([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT") ? 'checkbox' : 'radio',
  300. getCheckboxProps: (record: any) => {
  301. let { canvasType, pageElementsSpecList } = record
  302. let topData = pageElementsSpecList[0]
  303. if ([910].includes(creativeTemplateId) && canvasType === 'COMMON_PAGE') {
  304. return {
  305. disabled: true
  306. }
  307. }
  308. // 组件化创意
  309. if (deliveryMode === "DELIVERY_MODE_COMPONENT") {
  310. if ([910].includes(creativeTemplateId)) {
  311. return {
  312. disabled: false
  313. }
  314. }
  315. if (data[selectAdz - 1]?.pageList?.length >= mainJumpInfo?.length) {
  316. return {
  317. disabled: data[selectAdz - 1]?.pageList?.some((item: any) => item?.pageId === record?.pageId) ? false : true
  318. }
  319. } else {
  320. return {
  321. disabled: false
  322. }
  323. }
  324. }
  325. /**
  326. * 641 三图 642 4图 643 6图 311 一图
  327. * 721 素板视频 9:16 -> VERTICAL_VIDEO_NEW_916 720 722 横板视频 -> VIDEO_1280_960
  328. * 618 常规视频 ---> VIDEO
  329. * 1708 1707 ---> false
  330. */
  331. if (overrideCanvasHeadOption === 'OPTION_CREATIVE_OVERRIDE_CANVAS' || [1708, 1707].includes(creativeTemplateId)) {
  332. return {
  333. disabled: false
  334. }
  335. }
  336. if ([721].includes(creativeTemplateId) && canvasType === 'VERTICAL_VIDEO_NEW_916') {
  337. return {
  338. disabled: false
  339. }
  340. } else if ([720, 722].includes(creativeTemplateId) && canvasType === 'VIDEO_1280_960') {
  341. return {
  342. disabled: false
  343. }
  344. } else if ([618].includes(creativeTemplateId) && canvasType === 'VIDEO') {
  345. return {
  346. disabled: false
  347. }
  348. } else if ([311].includes(creativeTemplateId) && canvasType === "IMAGE" && topData.imageSpec.length === 1) {
  349. return {
  350. disabled: false
  351. }
  352. } else if ([641].includes(creativeTemplateId) && canvasType === "IMAGE" && topData.imageSpec.length === 3) {
  353. return {
  354. disabled: false
  355. }
  356. } else if ([642].includes(creativeTemplateId) && canvasType === "IMAGE" && topData.imageSpec.length === 4) {
  357. return {
  358. disabled: false
  359. }
  360. } else if ([643].includes(creativeTemplateId) && canvasType === "IMAGE" && topData.imageSpec.length === 6) {
  361. return {
  362. disabled: false
  363. }
  364. }
  365. return {
  366. disabled: true
  367. }
  368. },
  369. selectedRowKeys: data[selectAdz - 1]?.pageList?.map((item: any) => item?.pageId),
  370. hideSelectAll: deliveryMode === "DELIVERY_MODE_COMPONENT" ? creativeTemplateId === 910 ? false : true : false,
  371. onSelect: (record: any, selected: boolean) => {
  372. let newData = JSON.parse(JSON.stringify(data))
  373. if (([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT")) {
  374. let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
  375. if (selected) {
  376. selectedRows.push({ ...record })
  377. newData[selectAdz - 1]['pageList'] = selectedRows
  378. } else {
  379. newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => item.pageId !== record.pageId)
  380. }
  381. } else {
  382. newData[selectAdz - 1]['pageList'] = [record]
  383. }
  384. setData([...newData])
  385. },
  386. onSelectAll: (selected: boolean, _: any[], changeRows: { pageId: number }[]) => {
  387. let newData = JSON.parse(JSON.stringify(data))
  388. let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
  389. if (selected) {
  390. let newSelectAccData = [...selectedRows]
  391. changeRows.forEach((item: { pageId: number }) => {
  392. let index = newSelectAccData.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
  393. if (index === -1) {
  394. let data: any = { ...item }
  395. newSelectAccData.push(data)
  396. }
  397. })
  398. newData[selectAdz - 1]['pageList'] = newSelectAccData
  399. } else {
  400. newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => {
  401. let index = changeRows.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
  402. if (index !== -1) {
  403. return false
  404. } else {
  405. return true
  406. }
  407. })
  408. }
  409. setData([...newData])
  410. }
  411. }}
  412. />
  413. </div>
  414. <div className={style.center}>
  415. <Title level={5}>已选:{data[selectAdz - 1]?.pageList?.length || 0}</Title>
  416. <div className={style.select_content} style={{ height: 416 }}>
  417. {data[selectAdz - 1]?.pageList?.map((item: any) => <div key={item.pageId}>
  418. <Text ellipsis={{ tooltip: true }} className={style.marketingAssetName}>{item.pageName}</Text>
  419. <CloseOutlined className={style.close} onClick={() => {
  420. let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
  421. newData[selectAdz - 1].pageList = newData[selectAdz - 1]?.pageList?.filter((i: any) => i?.pageId !== item.pageId)
  422. setData(newData)
  423. }} />
  424. </div>)}
  425. </div>
  426. </div>
  427. </div>
  428. </Modal>
  429. }
  430. export default React.memo(PageModal)