index.tsx 21 KB

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