index.tsx 22 KB

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