index.tsx 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. import { useAjax } from '@/Hook/useAjax'
  2. import { AdStatusEnum, PromotedObjectType } from '@/services/launchAdq/enum'
  3. import { Col, Row, Input, Select, message, Space, Button, Popconfirm, Switch, notification } from 'antd'
  4. import React, { useEffect, useCallback, useState } from 'react'
  5. import TableData from '../../components/TableData'
  6. import tableConfig from './tableConfig'
  7. import { putAdqAdgroupsSync, getAdqAdgroupsList, delListAdqAdgroupsApi, newEditAdqAdgroupsDataApi, editAdqAdgroupsDataApi } from '@/services/launchAdq/adq'
  8. import { CopyOutlined, DeleteOutlined, FieldTimeOutlined, PauseCircleOutlined, PlayCircleOutlined, TransactionOutlined } from '@ant-design/icons'
  9. import UpdateAd from './updateAd'
  10. import Copy from './copy'
  11. import PlanDetail from '@/pages/adMonitor/adMonitorList/components/planDetail'
  12. type Props = {
  13. accountId: string,
  14. adAccountId: string,
  15. userId: string,
  16. queryParmas: {
  17. accountId?: string,//账户ID
  18. campaignId?: string,//计划ID
  19. adgroupId?: string,//广告ID
  20. adcreativeId?: string,//创意ID
  21. pageId?: string,//落地页ID
  22. targetingId?: string,//定向ID}
  23. },
  24. tableIdClick: (props: {
  25. activeKey: string, parma: {
  26. accountId?: string,//账户ID
  27. campaignId?: string,//计划ID
  28. adgroupId?: string,//广告ID
  29. adcreativeId?: string,//创意ID
  30. pageId?: string,//落地页ID
  31. targetingId?: string,//定向ID
  32. }
  33. }) => void
  34. }
  35. const Ad: React.FC<Props> = (props) => {
  36. /***********************/
  37. let { accountId, adAccountId, userId, tableIdClick, queryParmas } = props
  38. const [selectedRows, setSelectedRows] = useState<any[]>([])
  39. const [update, setUpdate] = useState<{ visible: boolean, title: string }>({ visible: false, title: '' })
  40. const [model, setModel] = useState(true)
  41. const [copyData, setCopyData] = useState<{ visible: boolean }>({ visible: false })
  42. const [detailShow, setDetailShow] = useState<boolean>(false)
  43. const [detailData, setDetailData] = useState<any>({})
  44. const [queryFrom, set_queryFrom] = useState<{
  45. pageNum: number;
  46. pageSize: number;
  47. accountIdList?: any[];
  48. adgroupName?: string;
  49. adgroupIdList?: any[];
  50. promotedObjectType?: string;
  51. isDeleted?: boolean
  52. campaignIdList?: any[]
  53. statusList?: any[],
  54. memoList?: any[]
  55. remarkList?: any[]
  56. }>({ pageNum: 1, pageSize: 20 })
  57. const listAjax = useAjax((params) => getAdqAdgroupsList(params), { formatResult: true })
  58. const syncAjax = useAjax((adAccountId) => putAdqAdgroupsSync(adAccountId))
  59. const delListAdqAdgroups = useAjax((params) => delListAdqAdgroupsApi(params))
  60. const editAdqAdgroupsData = useAjax((params) => newEditAdqAdgroupsDataApi(params))
  61. const editAdqAdgroups = useAjax((params) => editAdqAdgroupsDataApi(params))
  62. /************************/
  63. useEffect(() => {
  64. let { accountId, campaignId, adgroupId, ...obj } = queryParmas
  65. let new_queryParmas = {
  66. ...obj,
  67. accountIdList: accountId ? [accountId] : [],
  68. campaignIdList: campaignId ? [campaignId] : [],
  69. adgroupIdList: adgroupId ? [adgroupId] : []
  70. }
  71. getList({ pageNum: 1, pageSize: 20, ...new_queryParmas, accountIdList: accountId ? [accountId] : [] })
  72. }, [accountId, userId, queryParmas])
  73. // 获取列表
  74. const getList = useCallback((params: {
  75. pageNum: number;
  76. pageSize: number;
  77. accountIdList?: any[];
  78. adgroupName?: string;
  79. adgroupIdList?: any[];
  80. promotedObjectType?: string;
  81. isDeleted?: boolean
  82. campaignIdList?: any[]
  83. statusList?: any[],
  84. memoList?: any[]
  85. remarkList?: any[]
  86. }) => {
  87. if (!params.adgroupName || params.adgroupName !== listAjax?.params[0]?.adgroupName) {
  88. !params.adgroupName && delete params.adgroupName
  89. }
  90. listAjax.run({ ...params, userId })
  91. }, [userId, listAjax])
  92. // 同步
  93. const sync = useCallback(() => {
  94. if (!adAccountId) {
  95. message.error('请先选择要同步的广点通账号!')
  96. return
  97. }
  98. syncAjax.run({ adAccountId }).then(res => {
  99. res && listAjax.refresh()
  100. res ? message.success('同步成功!') : message.error('同步失败!')
  101. })
  102. }, [adAccountId, listAjax])
  103. /** 删除 */
  104. const deleteHandle = (type: 0 | 1, adgroupId?: number) => {
  105. delListAdqAdgroups.run({ adgroupIds: type === 1 ? selectedRows.map(item => item.adgroupId) : [adgroupId] }).then(res => {
  106. message.success('删除成功')
  107. setSelectedRows([])
  108. listAjax.refresh()
  109. })
  110. }
  111. /** 修改排期出价 */
  112. const editScheduling = () => {
  113. setUpdate({ visible: true, title: '批量修改' })
  114. }
  115. /** 修改排期出价 */
  116. const editDeepConversion = () => {
  117. setUpdate({ visible: true, title: '批量修改深度优化' })
  118. }
  119. // 单个启停
  120. const onChange = () => {
  121. listAjax.refresh()
  122. setSelectedRows([])
  123. }
  124. // 批量启停
  125. const adStatus = (type: 'play' | 'suspend') => {
  126. let params: any = {}
  127. if (type === 'play') {
  128. params.configuredStatus = 'AD_STATUS_NORMAL'
  129. params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_SUSPEND').map(item => item.adgroupId)
  130. } else {
  131. params.configuredStatus = 'AD_STATUS_SUSPEND'
  132. params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_NORMAL').map(item => item.adgroupId)
  133. }
  134. if (params.adgroupIds.length === 0) {
  135. message.warn(`所以账号都是${type === 'play' ? '启动' : '暂停'}状态,无需${type === 'play' ? '启动' : '暂停'}操作`)
  136. return
  137. }
  138. editAdqAdgroupsData.run(params).then(res => {
  139. message.success(`${type === 'play' ? '启动' : '暂停'}成功: ${res.success},失败: ${res.fail}`)//
  140. if (res?.fail) {
  141. notification.error({
  142. message: `${type === 'play' ? '启动' : '暂停'}失败`,
  143. description: `成功: ${res.success},修改失败${res.fail}条,失败的请到任务列表查看`,
  144. duration: 0
  145. });
  146. }
  147. listAjax.refresh()
  148. setSelectedRows([])
  149. })
  150. }
  151. // 批量复制
  152. const copyHandle = () => {
  153. setCopyData({ visible: true })
  154. }
  155. const handleSave = (row: any) => {
  156. const hide = message.loading(`广告“${row.adgroupId}”广告名称修改成<${row.adgroupName}>,修改中`, 0, () => {
  157. message.success('修改成功');
  158. });
  159. editAdqAdgroups.run({ adgroupIds: [row.adgroupId], adgroupName: row.adgroupName }).then(res => {
  160. message.success('修改广告名称成功')
  161. listAjax.refresh()
  162. hide()
  163. })
  164. }
  165. const details = (data: any) => {
  166. setDetailData(data)
  167. setDetailShow(true)
  168. }
  169. return <div>
  170. {/* 修改广告 */}
  171. {update.visible && <UpdateAd
  172. {...update}
  173. selectedRows={selectedRows}
  174. onChange={() => {
  175. setUpdate({ visible: false, title: '' })
  176. listAjax.refresh()
  177. setSelectedRows([])
  178. }}
  179. onClose={() => { setUpdate({ visible: false, title: '' }) }}
  180. />}
  181. {/* 复制广告 */}
  182. {copyData.visible && <Copy selectedRows={selectedRows} {...copyData} onClose={() => setCopyData({ visible: false })} onChange={() => { setCopyData({ visible: false }); listAjax.refresh(); setSelectedRows([]) }} />}
  183. <TableData
  184. isCard={false}
  185. columns={() => tableConfig(onChange, details, tableIdClick)}
  186. ajax={listAjax}
  187. syncAjax={sync}
  188. dataSource={listAjax?.data?.data?.records}
  189. loading={listAjax?.loading || syncAjax?.loading}
  190. scroll={{ y: 560 }}
  191. total={listAjax?.data?.data?.total}
  192. page={listAjax?.data?.data?.current}
  193. pageSize={listAjax?.data?.data?.size}
  194. myKey={'adgroupId'}
  195. handleSave={handleSave}
  196. leftChild={<Space direction='vertical'>
  197. <Row gutter={[10, 10]} align='middle'>
  198. <Col>
  199. <Input
  200. placeholder='广告账号'
  201. allowClear
  202. style={{ width: 150 }}
  203. onBlur={(e) => {
  204. let value = e.target.value
  205. let arr: any = []
  206. if (value) {
  207. value = value.replace(/[,,\s]/g, ',')
  208. arr = value.split(',').filter(a => a)
  209. }
  210. set_queryFrom({ ...queryFrom, accountIdList: arr })
  211. getList({ ...queryFrom, pageNum: 1, accountIdList: arr })
  212. }}
  213. onKeyDownCapture={(e: any) => {
  214. let key = e.key
  215. if (key === 'Enter') {
  216. let value = e.target.value
  217. let arr: any = []
  218. if (value) {
  219. value = value.replace(/[,,\s]/g, ',')
  220. arr = value.split(',').filter((a: any) => a)
  221. }
  222. set_queryFrom({ ...queryFrom, accountIdList: arr })
  223. getList({ ...queryFrom, pageNum: 1, accountIdList: arr })
  224. }
  225. }}
  226. onChange={(e) => {
  227. let value = e.target.value
  228. if (!value) {
  229. let arr: any = []
  230. if (value) {
  231. value = value.replace(/[,,\s]/g, ',')
  232. arr = value.split(',').filter((a: any) => a)
  233. }
  234. set_queryFrom({ ...queryFrom, accountIdList: arr })
  235. getList({ ...queryFrom, pageNum: 1, accountIdList: arr })
  236. }
  237. }}
  238. />
  239. </Col>
  240. <Col>
  241. <Input
  242. placeholder='广告名称'
  243. allowClear
  244. style={{ width: 150 }}
  245. onBlur={(e) => {
  246. let value = e.target.value
  247. set_queryFrom({ ...queryFrom, adgroupName: value })
  248. getList({ ...queryFrom, pageNum: 1, adgroupName: value })
  249. }}
  250. onKeyDownCapture={(e: any) => {
  251. let key = e.key
  252. if (key === 'Enter') {
  253. let value = e.target.value
  254. set_queryFrom({ ...queryFrom, adgroupName: value })
  255. getList({ ...queryFrom, pageNum: 1, adgroupName: value })
  256. }
  257. }}
  258. onChange={(e) => {
  259. let value = e.target.value
  260. if (!value) {
  261. set_queryFrom({ ...queryFrom, adgroupName: value })
  262. getList({ ...queryFrom, pageNum: 1, adgroupName: value })
  263. }
  264. }}
  265. />
  266. </Col>
  267. <Col>
  268. <Input
  269. placeholder='广告ID'
  270. allowClear
  271. style={{ width: 150 }}
  272. onBlur={(e) => {
  273. let value = e.target.value
  274. let arr: any = []
  275. if (value) {
  276. value = value.replace(/[,,\s]/g, ',')
  277. arr = value.split(',').filter((a: any) => a)
  278. }
  279. set_queryFrom({ ...queryFrom, adgroupIdList: arr })
  280. getList({ ...queryFrom, pageNum: 1, adgroupIdList: arr })
  281. }}
  282. onKeyDownCapture={(e: any) => {
  283. let key = e.key
  284. if (key === 'Enter') {
  285. let value = e.target.value
  286. let arr: any = []
  287. if (value) {
  288. value = value.replace(/[,,\s]/g, ',')
  289. arr = value.split(',').filter((a: any) => a)
  290. }
  291. set_queryFrom({ ...queryFrom, adgroupIdList: arr })
  292. getList({ ...queryFrom, pageNum: 1, adgroupIdList: arr })
  293. }
  294. }}
  295. onChange={(e) => {
  296. let value = e.target.value
  297. if (!value) {
  298. let arr: any = []
  299. if (value) {
  300. value = value.replace(/[,,\s]/g, ',')
  301. arr = value.split(',').filter((a: any) => a)
  302. }
  303. set_queryFrom({ ...queryFrom, adgroupIdList: arr })
  304. getList({ ...queryFrom, pageNum: 1, adgroupIdList: arr })
  305. }
  306. }}
  307. />
  308. </Col>
  309. <Col>
  310. <Input
  311. placeholder='计划ID'
  312. allowClear
  313. style={{ width: 150 }}
  314. onBlur={(e) => {
  315. let value = e.target.value
  316. let arr: any = []
  317. if (value) {
  318. value = value.replace(/[,,\s]/g, ',')
  319. arr = value.split(',').filter((a: any) => a)
  320. }
  321. set_queryFrom({ ...queryFrom, campaignIdList: arr })
  322. getList({ ...queryFrom, pageNum: 1, campaignIdList: arr })
  323. }}
  324. onKeyDownCapture={(e: any) => {
  325. let key = e.key
  326. if (key === 'Enter') {
  327. let value = e.target.value
  328. let arr: any = []
  329. if (value) {
  330. value = value.replace(/[,,\s]/g, ',')
  331. arr = value.split(',').filter((a: any) => a)
  332. }
  333. set_queryFrom({ ...queryFrom, campaignIdList: arr })
  334. getList({ ...queryFrom, pageNum: 1, campaignIdList: arr })
  335. }
  336. }}
  337. onChange={(e) => {
  338. let value = e.target.value
  339. if (!value) {
  340. let arr: any = []
  341. if (value) {
  342. value = value.replace(/[,,\s]/g, ',')
  343. arr = value.split(',').filter((a: any) => a)
  344. }
  345. set_queryFrom({ ...queryFrom, campaignIdList: arr })
  346. getList({ ...queryFrom, pageNum: 1, campaignIdList: arr })
  347. }
  348. }}
  349. />
  350. </Col>
  351. <Col>
  352. <Select
  353. placeholder='推广目标选择'
  354. style={{ width: 150 }}
  355. showSearch
  356. filterOption={(input: any, option: any) =>
  357. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  358. }
  359. allowClear
  360. onChange={(value: any) => {
  361. set_queryFrom({ ...queryFrom, promotedObjectType: value })
  362. getList({ ...queryFrom, pageNum: 1, promotedObjectType: value })
  363. }}
  364. >
  365. {Object.keys(PromotedObjectType).map(key => {
  366. return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
  367. })}
  368. </Select>
  369. </Col>
  370. <Col>
  371. <Select
  372. placeholder='是否已删除'
  373. style={{ width: 150 }}
  374. showSearch
  375. filterOption={(input: any, option: any) =>
  376. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  377. }
  378. allowClear
  379. onChange={(value: any) => {
  380. set_queryFrom({ ...queryFrom, isDeleted: value })
  381. getList({ ...queryFrom, pageNum: 1, isDeleted: value })
  382. }}
  383. >
  384. <Select.Option value={true}>已删除</Select.Option>
  385. <Select.Option value={false}>未删除</Select.Option>
  386. </Select>
  387. </Col>
  388. <Col>
  389. <Select
  390. placeholder='广告状态'
  391. mode="multiple"
  392. style={{ width: 200 }}
  393. showSearch
  394. filterOption={(input: any, option: any) =>
  395. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  396. }
  397. allowClear
  398. onChange={(value: any) => {
  399. set_queryFrom({ ...queryFrom, statusList: value })
  400. getList({ ...queryFrom, pageNum: 1, statusList: value })
  401. }}
  402. >
  403. {
  404. Object.keys(AdStatusEnum).map(key => {
  405. return <Select.Option value={key} key={key}>{AdStatusEnum[key]}</Select.Option>
  406. })
  407. }
  408. </Select>
  409. </Col>
  410. <Col>
  411. <Input
  412. placeholder='腾讯备注'
  413. allowClear
  414. style={{ width: 150 }}
  415. onBlur={(e) => {
  416. let value = e.target.value
  417. let arr: any = []
  418. if (value) {
  419. value = value.replace(/[,,\s]/g, ',')
  420. arr = value.split(',').filter(a => a)
  421. }
  422. set_queryFrom({ ...queryFrom, memoList: arr })
  423. getList({ ...queryFrom, pageNum: 1, memoList: arr })
  424. }}
  425. onKeyDownCapture={(e: any) => {
  426. let key = e.key
  427. if (key === 'Enter') {
  428. let value = e.target.value
  429. let arr: any = []
  430. if (value) {
  431. value = value.replace(/[,,\s]/g, ',')
  432. arr = value.split(',').filter((a: any) => a)
  433. }
  434. set_queryFrom({ ...queryFrom, memoList: arr })
  435. getList({ ...queryFrom, pageNum: 1, memoList: arr })
  436. }
  437. }}
  438. onChange={(e) => {
  439. let value = e.target.value
  440. if (!value) {
  441. let arr: any = []
  442. if (value) {
  443. value = value.replace(/[,,\s]/g, ',')
  444. arr = value.split(',').filter((a: any) => a)
  445. }
  446. set_queryFrom({ ...queryFrom, memoList: arr })
  447. getList({ ...queryFrom, pageNum: 1, memoList: arr })
  448. }
  449. }}
  450. />
  451. </Col>
  452. <Col>
  453. <Input
  454. placeholder='本地备注'
  455. allowClear
  456. style={{ width: 150 }}
  457. onBlur={(e) => {
  458. let value = e.target.value
  459. let arr: any = []
  460. if (value) {
  461. value = value.replace(/[,,\s]/g, ',')
  462. arr = value.split(',').filter(a => a)
  463. }
  464. set_queryFrom({ ...queryFrom, remarkList: arr })
  465. getList({ ...queryFrom, pageNum: 1, remarkList: arr })
  466. }}
  467. onKeyDownCapture={(e: any) => {
  468. let key = e.key
  469. if (key === 'Enter') {
  470. let value = e.target.value
  471. let arr: any = []
  472. if (value) {
  473. value = value.replace(/[,,\s]/g, ',')
  474. arr = value.split(',').filter((a: any) => a)
  475. }
  476. set_queryFrom({ ...queryFrom, remarkList: arr })
  477. getList({ ...queryFrom, pageNum: 1, remarkList: arr })
  478. }
  479. }}
  480. onChange={(e) => {
  481. let value = e.target.value
  482. if (!value) {
  483. let arr: any = []
  484. if (value) {
  485. value = value.replace(/[,,\s]/g, ',')
  486. arr = value.split(',').filter((a: any) => a)
  487. }
  488. set_queryFrom({ ...queryFrom, remarkList: arr })
  489. getList({ ...queryFrom, pageNum: 1, remarkList: arr })
  490. }
  491. }}
  492. />
  493. </Col>
  494. </Row>
  495. <Row gutter={[10, 10]} align='middle'>
  496. <Col>
  497. <Switch checkedChildren="普通模式" unCheckedChildren="深度优化" checked={model} onChange={(checked) => { setModel(checked); setSelectedRows([]) }} style={model ? {} : { background: '#67c23a' }} />
  498. </Col>
  499. {model ? <>
  500. <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<FieldTimeOutlined />} disabled={selectedRows.length === 0} onClick={editScheduling}>修改排期出价名称</Button></Col>
  501. <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<CopyOutlined />} disabled={selectedRows.length === 0} onClick={copyHandle}>批量复制</Button></Col>
  502. <Col><Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={editAdqAdgroupsData.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('play')}>启动广告</Button></Col>
  503. <Col><Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={editAdqAdgroupsData.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('suspend')}>暂停广告</Button></Col>
  504. <Col>
  505. <Popconfirm
  506. title="确定删除?"
  507. onConfirm={() => deleteHandle(1)}
  508. okText="是"
  509. cancelText="否"
  510. disabled={selectedRows.length === 0}
  511. >
  512. <Button danger type='primary' loading={delListAdqAdgroups.loading} icon={<DeleteOutlined />} disabled={selectedRows.length === 0}>删除</Button>
  513. </Popconfirm>
  514. </Col>
  515. </> : <Col><Button type='primary' icon={<TransactionOutlined />} disabled={selectedRows.length === 0} onClick={editDeepConversion}>修改深度优化ROI</Button></Col>}
  516. </Row>
  517. </Space>}
  518. rowSelection={{
  519. selectedRowKeys: selectedRows.map(item => item.adgroupId.toString()),
  520. getCheckboxProps: (record: any) => ({
  521. disabled: model ?
  522. record.status === 'STATUS_DELETED' :
  523. record.status === 'STATUS_DELETED' ||
  524. !(!model &&
  525. record?.promotedObjectType === 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT' &&
  526. record?.optimizationGoal === 'OPTIMIZATIONGOAL_FOLLOW' &&
  527. record?.deepConversionSpec?.deepConversionWorthSpec?.goal === 'GOAL_1DAY_PURCHASE_ROAS'
  528. )
  529. }),
  530. onChange: (selectedRowKeys: any, selectedRows: any) => {
  531. setSelectedRows(selectedRows)
  532. }
  533. }}
  534. onChange={(props: any) => {
  535. let { sortData, pagination } = props
  536. let { current, pageSize } = pagination
  537. // console.log(pagination)
  538. // console.log({...queryFrom, pageNum: current, pageSize })
  539. set_queryFrom({...queryFrom,pageNum:current,pageSize})
  540. getList({...queryFrom, pageNum: current, pageSize })
  541. }}
  542. />
  543. {detailShow && <PlanDetail visible={detailShow} onClose={() => { setDetailShow(false) }} data={detailData} />}
  544. </div>
  545. }
  546. export default Ad