indexOfficial.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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 { getAdqLandingPageOfficialListApi, getWXDownPageAuthInfoListApi } from "@/services/adqV3/global"
  8. import New1Radio from "../New1Radio"
  9. const { Title, Text } = Typography;
  10. /**
  11. * 获取adq落地页
  12. * @returns
  13. */
  14. interface Props {
  15. visible?: boolean,
  16. onClose?: () => void,
  17. onChange?: (value: { data: PULLIN.AccountCreateLogsProps[], landingPageType: 0 | 1 }) => void,
  18. adgroups: any
  19. dynamic: any
  20. data: PULLIN.AccountCreateLogsProps[]
  21. }
  22. const PageOfficialModal: React.FC<Props> = (props) => {
  23. /*************************/
  24. const { visible, onClose, data: data1, adgroups, onChange, dynamic } = props
  25. const { marketingGoal, marketingAssetOuterSpec: { marketingTargetType }, marketingCarrierType, siteSet } = adgroups
  26. const { creativeTemplateId, deliveryMode, creativeComponents: { mainJumpInfo }, landingPageType } = dynamic
  27. const [selectAdz, setSelectAdz] = useState<number>(1) // 选择广告主
  28. const [data, setData] = useState<PULLIN.AccountCreateLogsProps[]>(data1 || [])
  29. const [queryForm, setQueryForm] = useState<{ accountId?: number, pageName?: string, ownerUid?: number, pageSize: number, pageNum: number, isSqDownPage: boolean }>({ pageNum: 1, pageSize: 20, isSqDownPage: false })
  30. const [loading, setLoading] = useState<boolean>(false)
  31. const [wXDownPageAuthInfo, setWXDownPageAuthInfo] = useState<any>({})
  32. const [tableData, setTableData] = useState<any>({})
  33. const [pageAllocationType, setPageAllocationType] = useState<0 | 1>(landingPageType || 0)
  34. const listAjax = useAjax((params) => getAdqLandingPageOfficialListApi(params))
  35. /*************************/
  36. useEffect(() => {
  37. if (data?.length > 0) {
  38. setQueryForm({ ...queryForm, pageNum: 1, accountId: data[selectAdz - 1].accountId })
  39. }
  40. }, [])
  41. useEffect(() => {
  42. if (data?.length > 0) {
  43. setLoading(true)
  44. let accountDataList = data.map(item => item.accountId)
  45. let infoAjax = accountDataList.map(accountId => getWXDownPageAuthInfoListApi(accountId))
  46. let newData: any = {}
  47. Promise.all(infoAjax).then(res => {
  48. res?.forEach((item, index) => {
  49. newData[accountDataList[index]] = item?.data || []
  50. })
  51. setWXDownPageAuthInfo(newData)
  52. setLoading(false)
  53. }).catch(() => setLoading(false))
  54. }
  55. }, [])
  56. useEffect(() => {
  57. if (queryForm?.accountId) {
  58. getList()
  59. }
  60. }, [queryForm, marketingGoal, marketingTargetType, marketingCarrierType, siteSet, mainJumpInfo, creativeTemplateId])
  61. // 获取落地页列表
  62. const getList = () => {
  63. const params: any = {
  64. ...queryForm,
  65. pageStatus: 'NORMAL',
  66. marketingGoal,
  67. marketingTargetType,
  68. marketingCarrierType,
  69. siteSet: siteSet ? siteSet : ["SITE_SET_CHANNELS", "SITE_SET_MOMENTS", "SITE_SET_WECHAT", "SITE_SET_WECHAT_PLUGIN", "SITE_SET_KANDIAN", "SITE_SET_QQ_MUSIC_GAME", "SITE_SET_TENCENT_NEWS", "SITE_SET_TENCENT_VIDEO", "SITE_SET_MOBILE_UNION"],
  70. pageType: 'PAGE_TYPE_OFFICIAL',
  71. creativeTemplateId: creativeTemplateId || 311
  72. }
  73. const wechatChannelList: { wechatOfficialAccountId: string }[] = data[selectAdz - 1]?.wechatChannelList || []
  74. const marketingCarrierId = wechatChannelList?.[0]?.wechatOfficialAccountId
  75. if (marketingCarrierId) {
  76. params.marketingCarrierId = marketingCarrierId
  77. }
  78. if (params.isSqDownPage) {
  79. if (!params?.ownerUid) {
  80. setTableData({ total: 0, records: [] })
  81. return
  82. }
  83. } else {
  84. delete params?.ownerUid
  85. }
  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 = {
  125. ...queryForm,
  126. pageNum: 1,
  127. accountId: item.accountId,
  128. ownerUid: wXDownPageAuthInfo[item.accountId]?.[0]?.accountId,
  129. pageNameFull: pageName,
  130. pageStatus: 'NORMAL',
  131. marketingGoal,
  132. marketingTargetType,
  133. marketingCarrierType,
  134. siteSet: siteSet ? siteSet : ["SITE_SET_CHANNELS", "SITE_SET_MOMENTS", "SITE_SET_WECHAT", "SITE_SET_WECHAT_PLUGIN", "SITE_SET_KANDIAN", "SITE_SET_QQ_MUSIC_GAME", "SITE_SET_TENCENT_NEWS", "SITE_SET_TENCENT_VIDEO", "SITE_SET_MOBILE_UNION"],
  135. pageType: 'PAGE_TYPE_OFFICIAL',
  136. creativeTemplateId: creativeTemplateId || 311
  137. }
  138. if (!params.isSqDownPage) {
  139. delete params?.ownerUid
  140. }
  141. delete params.isSqDownPage
  142. return getAdqLandingPageOfficialListApi(params)
  143. })
  144. Promise.all(ajax).then(res => {
  145. if (res && Array.isArray(res)) {
  146. res.forEach((item: any, index) => {
  147. let records = item?.data?.records
  148. if (Array.isArray(records) && records?.length > 0) {
  149. let record = records.find(item => (item.pageName === pageName && item.disableCode === 0))
  150. let filterD = filterData[index]
  151. newData = newData.map(item => {
  152. if (item.accountId.toString() === filterD.accountId.toString() && record) {
  153. if (pageIndex === 0) {
  154. return { ...item, pageList: [{ ...record, id: record.pageId }] }
  155. } else {
  156. let pageList = item.pageList || []
  157. pageList.push({ ...record, id: record.pageId })
  158. return { ...item, pageList }
  159. }
  160. }
  161. return item
  162. })
  163. }
  164. })
  165. if (count > pageIndex + 1) {
  166. setPageData(pageIndex + 1)
  167. }
  168. if (count === pageIndex + 1) {
  169. setData(newData)
  170. }
  171. }
  172. message.success('设置完成');
  173. hide()
  174. })
  175. }
  176. setPageData(0)
  177. }
  178. return <Modal
  179. title={<Space>
  180. <strong>选择落地页</strong>
  181. {data?.length > 1 && <Button style={{ padding: 0, margin: 0 }} disabled={!data[selectAdz - 1]['pageList']?.length} onClick={setOnekey} type="link" loading={listAjax.loading}>
  182. <Space>
  183. <span style={{ fontSize: 12 }}>一键设置</span>
  184. <Tooltip color="#FFF" overlayInnerStyle={{ color: '#000' }} title="设置其它账号有相同名称的落地页为那个账号的落地页(需要落地页名称相同,否则不设置,注意只会根据当前条件去搜索,比如选择了被授权落地页,只会去被授权落地页里找对应落地页)">
  185. <QuestionCircleOutlined />
  186. </Tooltip>
  187. </Space>
  188. </Button>}
  189. <Button
  190. type="link"
  191. danger
  192. disabled={!data?.some(item => item.pageList?.length)}
  193. onClick={() => {
  194. setData(data => data.map(item => ({ ...item, pageList: [] })))
  195. }}
  196. >全部清空</Button>
  197. <Text type="warning">请确保账号开通了权限</Text>
  198. </Space>}
  199. open={visible}
  200. onCancel={() => { onClose && onClose() }}
  201. onOk={handleOk}
  202. width={1100}
  203. className={`${style.SelectPackage} modalResetCss`}
  204. bodyStyle={{ padding: '0 10px 0 10px' }}
  205. >
  206. {[910].includes(creativeTemplateId) && <div className={style.pageType}>
  207. <Space size={12}>
  208. <strong>落地页分配规则</strong>
  209. <New1Radio
  210. data={[{ label: '全部相同', value: 0 }, { label: '平均分配', value: 1 }]}
  211. value={pageAllocationType}
  212. onChange={(e) => {
  213. setPageAllocationType(e)
  214. }}
  215. />
  216. </Space>
  217. </div>}
  218. <div className={style.content}>
  219. <div className={style.left}>
  220. <h4 className={style.title}>媒体账户</h4>
  221. <div className={style.accountIdList}>
  222. {data?.map((item, index) => (
  223. <div key={index} onClick={() => { if (!loading) handleSelectAdz(index + 1, item) }} className={`${style.accItem} ${selectAdz === index + 1 && style.select} `}>
  224. {item?.accountId}
  225. {data[index].pageList?.length > 0 && <CheckOutlined style={{ color: '#1890ff' }} />}
  226. </div>
  227. ))}
  228. </div>
  229. </div>
  230. <div className={style.right}>
  231. <Space style={{ marginBottom: 10 }}>
  232. <Checkbox
  233. onChange={(e) => {
  234. if (e.target.checked && wXDownPageAuthInfo && Object.keys(wXDownPageAuthInfo).length) {
  235. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: wXDownPageAuthInfo[data[selectAdz - 1].accountId]?.[0]?.accountId, isSqDownPage: true })
  236. } else {
  237. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: undefined, isSqDownPage: false })
  238. }
  239. }}
  240. checked={queryForm.isSqDownPage}
  241. ><span style={{ fontSize: 12 }}>被授权落地页</span></Checkbox>
  242. {queryForm.isSqDownPage && <Select
  243. placeholder='选择原生页授权方信息'
  244. style={{ width: 210 }}
  245. showSearch
  246. filterOption={(input: any, option: any) =>
  247. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  248. }
  249. allowClear
  250. onChange={(value) => {
  251. setQueryForm({ ...queryForm, pageNum: 1, ownerUid: value })
  252. }}
  253. value={queryForm?.ownerUid}
  254. dropdownMatchSelectWidth={false}
  255. >
  256. {wXDownPageAuthInfo?.[data[selectAdz - 1].accountId]?.map((item: { accountId: number; accountName: string }) => {
  257. return <Select.Option value={item.accountId} key={item.accountId}>{item.accountName}({item.accountId})</Select.Option>
  258. })}
  259. </Select>}
  260. <Input value={queryForm?.pageName} style={{ width: 150 }} allowClear placeholder='请输入落地页名称' onChange={(e) => setQueryForm({ ...queryForm, pageNum: 1, pageName: e.target.value })} />
  261. <Button style={{ padding: 0, margin: 0 }} icon={<SyncOutlined />} type='link' loading={listAjax?.loading} onClick={() => { listAjax?.refresh() }}><span style={{ fontSize: 12 }}>刷新</span></Button>
  262. </Space>
  263. <Table
  264. columns={columns(2)}
  265. dataSource={tableData?.records}
  266. size="small"
  267. loading={listAjax?.loading || loading}
  268. scroll={{ y: 400 }}
  269. bordered
  270. rowKey={'pageId'}
  271. pagination={{
  272. total: tableData?.total,
  273. defaultPageSize: 20,
  274. current: tableData?.current,
  275. pageSize: tableData?.size
  276. }}
  277. onChange={(pagination) => {
  278. const { current, pageSize } = pagination
  279. setQueryForm({ ...queryForm, pageNum: current as number, pageSize: pageSize as number || 20 })
  280. }}
  281. /**
  282. * canvasType
  283. * VIDEO
  284. * VIDEO_1280_960 横板1280*960
  285. * VIDEO_1280_720 横板1280*720
  286. * VERTICAL_VIDEO_NEW_916 9:16 视频
  287. * COMMON_PAGE 引用外层素材原生页
  288. * IMAGE
  289. */
  290. rowSelection={{
  291. type: ([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT") ? 'checkbox' : 'radio',
  292. getCheckboxProps: (record: any) => {
  293. let { disableCode } = record
  294. if (disableCode !== 0) {
  295. return {
  296. disabled: true
  297. }
  298. }
  299. // 组件化创意
  300. if (deliveryMode === "DELIVERY_MODE_COMPONENT") {
  301. if (data[selectAdz - 1]?.pageList?.length >= mainJumpInfo?.length) {
  302. return {
  303. disabled: data[selectAdz - 1]?.pageList?.some((item: any) => item?.pageId === record?.pageId) ? false : true
  304. }
  305. } else {
  306. return {
  307. disabled: false
  308. }
  309. }
  310. }
  311. return {
  312. disabled: false
  313. }
  314. },
  315. selectedRowKeys: data[selectAdz - 1]?.pageList?.map((item: any) => item?.pageId),
  316. hideSelectAll: deliveryMode === "DELIVERY_MODE_COMPONENT",
  317. onSelect: (record: any, selected: boolean) => {
  318. let newData = JSON.parse(JSON.stringify(data))
  319. if (([910].includes(creativeTemplateId) || deliveryMode === "DELIVERY_MODE_COMPONENT")) {
  320. let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
  321. if (selected) {
  322. selectedRows.push({ ...record })
  323. newData[selectAdz - 1]['pageList'] = selectedRows
  324. } else {
  325. newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => item.pageId !== record.pageId)
  326. }
  327. } else {
  328. newData[selectAdz - 1]['pageList'] = [record]
  329. }
  330. setData([...newData])
  331. },
  332. onSelectAll: (selected: boolean, _: any[], changeRows: { pageId: number }[]) => {
  333. let newData = JSON.parse(JSON.stringify(data))
  334. let selectedRows = newData?.[selectAdz - 1]?.['pageList'] || []
  335. if (selected) {
  336. let newSelectAccData = [...selectedRows]
  337. changeRows.forEach((item: { pageId: number }) => {
  338. let index = newSelectAccData.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
  339. if (index === -1) {
  340. let data: any = { ...item }
  341. newSelectAccData.push(data)
  342. }
  343. })
  344. newData[selectAdz - 1]['pageList'] = newSelectAccData
  345. } else {
  346. newData[selectAdz - 1]['pageList'] = selectedRows.filter((item: { pageId: number }) => {
  347. let index = changeRows.findIndex((ite: { pageId: number }) => ite.pageId === item.pageId)
  348. if (index !== -1) {
  349. return false
  350. } else {
  351. return true
  352. }
  353. })
  354. }
  355. setData([...newData])
  356. }
  357. }}
  358. />
  359. </div>
  360. <div className={style.center}>
  361. <Title level={5}>已选:{data[selectAdz - 1]?.pageList?.length || 0}</Title>
  362. <div className={style.select_content} style={{ height: 416 }}>
  363. {data[selectAdz - 1]?.pageList?.map((item: any) => <div key={item.pageId}>
  364. <Text ellipsis={{ tooltip: true }} className={style.marketingAssetName}>{item.pageName}</Text>
  365. <CloseOutlined className={style.close} onClick={() => {
  366. let newData: PULLIN.AccountCreateLogsProps[] = JSON.parse(JSON.stringify(data))
  367. newData[selectAdz - 1].pageList = newData[selectAdz - 1]?.pageList?.filter((i: any) => i?.pageId !== item.pageId)
  368. setData(newData)
  369. }} />
  370. </div>)}
  371. </div>
  372. </div>
  373. </div>
  374. </Modal>
  375. }
  376. export default React.memo(PageOfficialModal)