index.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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, 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. type Props = {
  12. accountId: string,
  13. adAccountId: string,
  14. userId: string,
  15. queryParmas: {
  16. accountId?: string,//账户ID
  17. campaignId?: string,//计划ID
  18. adgroupId?: string,//广告ID
  19. adcreativeId?: string,//创意ID
  20. pageId?: string,//落地页ID
  21. targetingId?: string,//定向ID}
  22. },
  23. tableIdClick: (props: {
  24. activeKey: string, parma: {
  25. accountId?: string,//账户ID
  26. campaignId?: string,//计划ID
  27. adgroupId?: string,//广告ID
  28. adcreativeId?: string,//创意ID
  29. pageId?: string,//落地页ID
  30. targetingId?: string,//定向ID
  31. }
  32. }) => void
  33. }
  34. const Ad: React.FC<Props> = (props) => {
  35. /***********************/
  36. let { accountId, adAccountId, userId, tableIdClick, queryParmas } = props
  37. const [selectedRows, setSelectedRows] = useState<any[]>([])
  38. const [update, setUpdate] = useState<{ visible: boolean, title: string }>({ visible: false, title: '' })
  39. const [model, setModel] = useState(true)
  40. const [copyData, setCopyData] = useState<{ visible: boolean }>({ visible: false })
  41. const listAjax = useAjax((params) => getAdqAdgroupsList(params), { formatResult: true })
  42. const syncAjax = useAjax((adAccountId) => putAdqAdgroupsSync(adAccountId))
  43. const delListAdqAdgroups = useAjax((params) => delListAdqAdgroupsApi(params))
  44. const editAdqAdgroupsData = useAjax((params) => editAdqAdgroupsDataApi(params))
  45. /************************/
  46. useEffect(() => {
  47. let {accountId,campaignId,adgroupId,...obj} = queryParmas
  48. let new_queryParmas = {
  49. ...obj,
  50. accountIdList:accountId?[accountId]:[],
  51. campaignIdList:campaignId?[campaignId]:[],
  52. adgroupIdList:adgroupId?[adgroupId]:[]
  53. }
  54. getList({ pageNum: 1, pageSize: 20, ...new_queryParmas,accountIdList:accountId?[accountId]:[] })
  55. }, [accountId, userId, queryParmas])
  56. // 获取列表
  57. const getList = useCallback((params: {
  58. pageNum: number;
  59. pageSize: number;
  60. accountIdList?: any[];
  61. adgroupName?: string;
  62. adgroupIdList?: any[];
  63. promotedObjectType?: string;
  64. isDeleted?: boolean
  65. campaignIdList?: any[]
  66. statusList?:any[]
  67. }) => {
  68. if (!params.adgroupName || params.adgroupName !== listAjax?.params[0]?.adgroupName) {
  69. !params.adgroupName && delete params.adgroupName
  70. listAjax.run({ ...params, userId })
  71. }
  72. }, [userId, listAjax])
  73. // 同步
  74. const sync = useCallback(() => {
  75. if (!adAccountId) {
  76. message.error('请先选择要同步的广点通账号!')
  77. return
  78. }
  79. syncAjax.run({ adAccountId }).then(res => {
  80. res && listAjax.refresh()
  81. res ? message.success('同步成功!') : message.error('同步失败!')
  82. })
  83. }, [adAccountId, listAjax])
  84. /** 删除 */
  85. const deleteHandle = (type: 0 | 1, adgroupId?: number) => {
  86. delListAdqAdgroups.run({ adgroupIds: type === 1 ? selectedRows.map(item => item.adgroupId) : [adgroupId] }).then(res => {
  87. message.success('删除成功')
  88. setSelectedRows([])
  89. listAjax.refresh()
  90. })
  91. }
  92. /** 修改排期出价 */
  93. const editScheduling = () => {
  94. setUpdate({ visible: true, title: '批量修改' })
  95. }
  96. /** 修改排期出价 */
  97. const editDeepConversion = () => {
  98. setUpdate({ visible: true, title: '批量修改深度优化' })
  99. }
  100. // 单个启停
  101. const onChange = () => {
  102. listAjax.refresh()
  103. setSelectedRows([])
  104. }
  105. // 批量启停
  106. const adStatus = (type: 'play' | 'suspend') => {
  107. let params: any = {}
  108. if (type === 'play') {
  109. params.configuredStatus = 'AD_STATUS_NORMAL'
  110. params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_SUSPEND').map(item => item.adgroupId)
  111. } else {
  112. params.configuredStatus = 'AD_STATUS_SUSPEND'
  113. params.adgroupIds = selectedRows.filter((item: { configuredStatus: string, adgroupId: number }) => item.configuredStatus === 'AD_STATUS_NORMAL').map(item => item.adgroupId)
  114. }
  115. if (params.adgroupIds.length === 0) {
  116. message.warn(`所以账号都是${type === 'play' ? '启动' : '暂停'}状态,无需${type === 'play' ? '启动' : '暂停'}操作`)
  117. return
  118. }
  119. editAdqAdgroupsData.run(params).then(res => {
  120. message.success(`${type === 'play' ? '启动' : '暂停'}操作完成.结果请在操作记录查询!`)//成功: ${res.success},失败: ${res.fail}
  121. if (res?.fail) {
  122. notification.error({
  123. message: `${type === 'play' ? '启动' : '暂停'}失败`,
  124. description: `成功: ${res.success},修改失败${res.fail}条,失败的请到任务列表查看`,
  125. duration: 0
  126. });
  127. }
  128. listAjax.refresh()
  129. setSelectedRows([])
  130. })
  131. }
  132. // 批量复制
  133. const copyHandle = () => {
  134. setCopyData({ visible: true })
  135. }
  136. return <div>
  137. {/* 修改广告 */}
  138. {update.visible && <UpdateAd
  139. {...update}
  140. selectedRows={selectedRows}
  141. onChange={() => {
  142. setUpdate({ visible: false, title: '' })
  143. listAjax.refresh()
  144. setSelectedRows([])
  145. }}
  146. onClose={() => { setUpdate({ visible: false, title: '' }) }}
  147. />}
  148. {/* 复制广告 */}
  149. {copyData.visible && <Copy selectedRows={selectedRows} {...copyData} onClose={() => setCopyData({ visible: false })} onChange={() => { setCopyData({ visible: false }); listAjax.refresh(); setSelectedRows([]) }} />}
  150. <TableData
  151. isCard={false}
  152. columns={() => tableConfig(onChange, tableIdClick)}
  153. ajax={listAjax}
  154. syncAjax={sync}
  155. dataSource={listAjax?.data?.data?.records}
  156. loading={listAjax?.loading || syncAjax?.loading}
  157. scroll={{ y: 560 }}
  158. total={listAjax?.data?.data?.total}
  159. page={listAjax?.data?.data?.current}
  160. pageSize={listAjax?.data?.data?.size}
  161. myKey={'adgroupId'}
  162. leftChild={<Space direction='vertical'>
  163. <Row gutter={[10, 10]} align='middle'>
  164. <Col>
  165. <Input
  166. placeholder='广告账号'
  167. allowClear
  168. style={{ width: 150 }}
  169. onBlur={(e) => {
  170. let value = e.target.value
  171. let arr:any = []
  172. if(value){
  173. value = value.replace(/[,,\s]/g,',')
  174. arr = value.split(',').filter(a=>a)
  175. }
  176. getList({ pageNum: 1, pageSize: 20, accountIdList: arr })
  177. }}
  178. onKeyDownCapture={(e: any) => {
  179. let key = e.key
  180. if (key === 'Enter') {
  181. let value = e.target.value
  182. let arr:any = []
  183. if(value){
  184. value = value.replace(/[,,\s]/g,',')
  185. arr = value.split(',').filter((a: any)=>a)
  186. }
  187. getList({ pageNum: 1, pageSize: 20, accountIdList: arr })
  188. }
  189. }}
  190. onChange={(e) => {
  191. let value = e.target.value
  192. if (!value) {
  193. let arr:any = []
  194. if(value){
  195. value = value.replace(/[,,\s]/g,',')
  196. arr = value.split(',').filter((a: any)=>a)
  197. }
  198. getList({ pageNum: 1, pageSize: 20, accountIdList: arr })
  199. }
  200. }}
  201. />
  202. </Col>
  203. <Col>
  204. <Input
  205. placeholder='广告名称'
  206. allowClear
  207. style={{ width: 150 }}
  208. onBlur={(e) => {
  209. let value = e.target.value
  210. getList({ pageNum: 1, pageSize: 20, adgroupName: value })
  211. }}
  212. onKeyDownCapture={(e: any) => {
  213. let key = e.key
  214. if (key === 'Enter') {
  215. let value = e.target.value
  216. getList({ pageNum: 1, pageSize: 20, adgroupName: value })
  217. }
  218. }}
  219. onChange={(e) => {
  220. let value = e.target.value
  221. if (!value) {
  222. getList({ pageNum: 1, pageSize: 20, adgroupName: value })
  223. }
  224. }}
  225. />
  226. </Col>
  227. <Col>
  228. <Input
  229. placeholder='广告ID'
  230. allowClear
  231. style={{ width: 150 }}
  232. onBlur={(e) => {
  233. let value = e.target.value
  234. let arr:any = []
  235. if(value){
  236. value = value.replace(/[,,\s]/g,',')
  237. arr = value.split(',').filter((a: any)=>a)
  238. }
  239. getList({ pageNum: 1, pageSize: 20, adgroupIdList: arr })
  240. }}
  241. onKeyDownCapture={(e: any) => {
  242. let key = e.key
  243. if (key === 'Enter') {
  244. let value = e.target.value
  245. let arr:any = []
  246. if(value){
  247. value = value.replace(/[,,\s]/g,',')
  248. arr = value.split(',').filter((a: any)=>a)
  249. }
  250. getList({ pageNum: 1, pageSize: 20, adgroupIdList: arr })
  251. }
  252. }}
  253. onChange={(e) => {
  254. let value = e.target.value
  255. if (!value) {
  256. let arr:any = []
  257. if(value){
  258. value = value.replace(/[,,\s]/g,',')
  259. arr = value.split(',').filter((a: any)=>a)
  260. }
  261. getList({ pageNum: 1, pageSize: 20, adgroupIdList: arr })
  262. }
  263. }}
  264. />
  265. </Col>
  266. <Col>
  267. <Input
  268. placeholder='计划ID'
  269. allowClear
  270. style={{ width: 150 }}
  271. onBlur={(e) => {
  272. let value = e.target.value
  273. let arr:any = []
  274. if(value){
  275. value = value.replace(/[,,\s]/g,',')
  276. arr = value.split(',').filter((a: any)=>a)
  277. }
  278. getList({ pageNum: 1, pageSize: 20, campaignIdList: arr })
  279. }}
  280. onKeyDownCapture={(e: any) => {
  281. let key = e.key
  282. if (key === 'Enter') {
  283. let value = e.target.value
  284. let arr:any = []
  285. if(value){
  286. value = value.replace(/[,,\s]/g,',')
  287. arr = value.split(',').filter((a: any)=>a)
  288. }
  289. getList({ pageNum: 1, pageSize: 20, campaignIdList: arr })
  290. }
  291. }}
  292. onChange={(e) => {
  293. let value = e.target.value
  294. if (!value) {
  295. let arr:any = []
  296. if(value){
  297. value = value.replace(/[,,\s]/g,',')
  298. arr = value.split(',').filter((a: any)=>a)
  299. }
  300. getList({ pageNum: 1, pageSize: 20, campaignIdList: arr })
  301. }
  302. }}
  303. />
  304. </Col>
  305. <Col>
  306. <Select
  307. placeholder='推广目标选择'
  308. style={{ width: 150 }}
  309. showSearch
  310. filterOption={(input: any, option: any) =>
  311. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  312. }
  313. allowClear
  314. onChange={(value: any) => {
  315. getList({ pageNum: 1, pageSize: 20, promotedObjectType: value })
  316. }}
  317. >
  318. {Object.keys(PromotedObjectType).map(key => {
  319. return <Select.Option value={key} key={key}>{PromotedObjectType[key]}</Select.Option>
  320. })}
  321. </Select>
  322. </Col>
  323. <Col>
  324. <Select
  325. placeholder='是否已删除'
  326. style={{ width: 150 }}
  327. showSearch
  328. filterOption={(input: any, option: any) =>
  329. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  330. }
  331. allowClear
  332. onChange={(value: any) => {
  333. getList({ pageNum: 1, pageSize: 20, isDeleted: value })
  334. }}
  335. >
  336. <Select.Option value={true}>已删除</Select.Option>
  337. <Select.Option value={false}>未删除</Select.Option>
  338. </Select>
  339. </Col>
  340. <Col>
  341. <Select
  342. placeholder='广告状态'
  343. mode="multiple"
  344. style={{ width: 200 }}
  345. showSearch
  346. filterOption={(input: any, option: any) =>
  347. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  348. }
  349. allowClear
  350. onChange={(value: any) => {
  351. getList({ pageNum: 1, pageSize: 20, statusList: value })
  352. }}
  353. >
  354. {
  355. Object.keys(AdStatusEnum).map(key=>{
  356. return <Select.Option value={key} key={key}>{AdStatusEnum[key]}</Select.Option>
  357. })
  358. }
  359. </Select>
  360. </Col>
  361. </Row>
  362. <Row gutter={[10, 10]} align='middle'>
  363. <Col>
  364. <Switch checkedChildren="普通模式" unCheckedChildren="深度优化" checked={model} onChange={(checked) => { setModel(checked); setSelectedRows([]) }} style={model ? {} : { background: '#67c23a' }} />
  365. </Col>
  366. {model ? <>
  367. <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<FieldTimeOutlined />} disabled={selectedRows.length === 0} onClick={editScheduling}>修改排期出价</Button></Col>
  368. <Col><Button type='primary' style={{ background: '#1890ff' }} icon={<CopyOutlined />} disabled={selectedRows.length === 0} onClick={copyHandle}>批量复制</Button></Col>
  369. <Col><Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={editAdqAdgroupsData.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('play')}>启动广告</Button></Col>
  370. <Col><Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={editAdqAdgroupsData.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => adStatus('suspend')}>暂停广告</Button></Col>
  371. <Col>
  372. <Popconfirm
  373. title="确定删除?"
  374. onConfirm={() => deleteHandle(1)}
  375. okText="是"
  376. cancelText="否"
  377. disabled={selectedRows.length === 0}
  378. >
  379. <Button danger type='primary' loading={delListAdqAdgroups.loading} icon={<DeleteOutlined />} disabled={selectedRows.length === 0}>删除</Button>
  380. </Popconfirm>
  381. </Col>
  382. </> : <Col><Button type='primary' icon={<TransactionOutlined />} disabled={selectedRows.length === 0} onClick={editDeepConversion}>修改深度优化ROI</Button></Col>}
  383. </Row>
  384. </Space>}
  385. rowSelection={{
  386. selectedRowKeys: selectedRows.map(item => item.adgroupId.toString()),
  387. getCheckboxProps: (record: any) => ({
  388. disabled: model ?
  389. record.status === 'STATUS_DELETED' :
  390. record.status === 'STATUS_DELETED' ||
  391. !(!model &&
  392. record?.promotedObjectType === 'PROMOTED_OBJECT_TYPE_WECHAT_OFFICIAL_ACCOUNT' &&
  393. record?.optimizationGoal === 'OPTIMIZATIONGOAL_FOLLOW' &&
  394. record?.deepConversionSpec?.deepConversionWorthSpec?.goal === 'GOAL_1DAY_PURCHASE_ROAS'
  395. )
  396. }),
  397. onChange: (selectedRowKeys: any, selectedRows: any) => {
  398. setSelectedRows(selectedRows)
  399. }
  400. }}
  401. onChange={(props: any) => {
  402. let { sortData, pagination } = props
  403. let { current, pageSize } = pagination
  404. getList({ pageNum: current, pageSize })
  405. }}
  406. />
  407. </div>
  408. }
  409. export default Ad