index.tsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. import { useAjax } from "@/Hook/useAjax"
  2. import { delBatchCreativeApi, getDynamicCreativeV3ListApi, syncBatchCreativeApi, updateBatchDynamicCreativesInfoApi } from "@/services/launchAdq/adqv3"
  3. import { Badge, Button, Col, DatePicker, Form, Input, Modal, Popconfirm, Row, Select, Space, Table, message } from "antd"
  4. import React, { useEffect, useState } from "react"
  5. import { txDynamicConfig } from "../config"
  6. import tableConfig from "./tableConfig"
  7. import TableData from "@/pages/launchSystemNew/components/TableData"
  8. import ReviewDetails from "./reviewDetails"
  9. import { DeleteOutlined, PauseCircleOutlined, PlayCircleOutlined } from "@ant-design/icons"
  10. import '../../tencentAdPutIn/index.less'
  11. import HandleLog from "./handleLog"
  12. import moment from "moment"
  13. import RetriaModal from "./retriaModal"
  14. import ReplacePage from "./replacePage"
  15. import ReplaceGfPage from "./replaceGfPage"
  16. /** 审核结果 */
  17. export const AD_STATUS = {
  18. NORMAL: <Badge status="success" text="审核通过" />,
  19. PENDING: <Badge status="default" text="审核中" />,
  20. DENIED: <Badge status="error" text="有违规" />,
  21. PARTIALLY_NORMAL: <Badge status="warning" text="部分审核通过" />,
  22. }
  23. const Creative: React.FC<ADQV3.CreativeProps> = ({ queryForm, setQueryForm, userId }) => {
  24. /*********************************/
  25. const [form] = Form.useForm();
  26. const [dynimicData, setDynamicData] = useState<any>({})
  27. const [dynimicVisible, setDynamicVisible] = useState<boolean>(false)
  28. const [selectedRows, setSelectedRows] = useState<any[]>([])
  29. const [failIdList, setFailIdList] = useState<{ adgroupId: number, code: number, message: string, messageCn: string }[]>([])
  30. const [failVisible, setFailVisible] = useState<boolean>(false)
  31. const [logVisible, setLogVisible] = useState<boolean>(false)
  32. const [handleType, setHandleType] = useState<number>(1)
  33. const [replaceVisible, setReplaceVisible] = useState<boolean>(false)
  34. const [replaceGfVisible, setReplaceGfVisible] = useState<boolean>(false)
  35. const getDynamicCreativeV3List = useAjax((params) => getDynamicCreativeV3ListApi(params), { formatResult: true })
  36. const delBatchCreative = useAjax((params) => delBatchCreativeApi(params))
  37. const updateBatchDynamicCreativesInfo = useAjax((params) => updateBatchDynamicCreativesInfoApi(params))
  38. const syncBatchCreative = useAjax((params) => syncBatchCreativeApi(params))
  39. /*********************************/
  40. useEffect(() => {
  41. form.setFieldsValue({ adgroupId: queryForm.adgroupId })
  42. }, [queryForm.adgroupId])
  43. useEffect(() => {
  44. let params: any = { ...queryForm, userId }
  45. if (params?.accountId) {
  46. params.accountId = params.accountId?.split(/[,,\s\n]+/)
  47. }
  48. getDynamicCreativeV3List.run(params)
  49. }, [userId, queryForm])
  50. const onFinish = (values: any) => {
  51. console.log(values)
  52. const { date, ...value } = values
  53. let newQueryForm = { ...queryForm, ...value }
  54. if (date?.length) {
  55. newQueryForm.beginDate = moment(date[0]).format('YYYY-MM-DD')
  56. newQueryForm.endDate = moment(date[1]).format('YYYY-MM-DD')
  57. } else {
  58. delete newQueryForm?.beginDate
  59. delete newQueryForm?.endDate
  60. }
  61. setQueryForm(newQueryForm)
  62. }
  63. const reviewStatusDetails = (value: any) => {
  64. setDynamicData(value)
  65. setDynamicVisible(true)
  66. }
  67. const dynamicHandle = (type: '删除' | '启动' | '暂停', data?: any) => {
  68. let accountAdgroupMaps = data ? [data.accountId + ',' + data.dynamicCreativeId] : [...new Set(selectedRows?.map(item => item.accountId + ',' + item.dynamicCreativeId))]
  69. let hide: any
  70. if (data) {
  71. hide = message.loading(`正在设置...`, 0, () => {
  72. message.success('设置成功');
  73. });
  74. }
  75. switch (type) {
  76. case '删除':
  77. delBatchCreative.run({ accountAdgroupMaps }).then(res => {
  78. if (res?.failIdList?.length === 0) {
  79. message.success(`修改操作完成!`)
  80. getDynamicCreativeV3List.refresh()
  81. setSelectedRows([])
  82. } else {
  83. setFailIdList(res?.list || [])
  84. setFailVisible(true)
  85. }
  86. })
  87. break
  88. case '启动':
  89. case '暂停':
  90. updateBatchDynamicCreativesInfo.run({ accountAdgroupMaps, suspend: type === '暂停' }).then(res => {
  91. if (res?.failIdList?.length === 0) {
  92. message.success(`修改操作完成!`)
  93. getDynamicCreativeV3List.refresh()
  94. setSelectedRows([])
  95. } else {
  96. setFailIdList(res?.list || [])
  97. setFailVisible(true)
  98. }
  99. if (hide) {
  100. hide()
  101. }
  102. })
  103. break
  104. }
  105. }
  106. const sync = () => {
  107. if (selectedRows?.length > 0) {
  108. const hide = message.loading(`正在同步...`, 0, () => {
  109. message.success('设置成功');
  110. });
  111. let accountAdgroupMaps = [...new Set(selectedRows?.map(item => item.accountId + ',' + item.dynamicCreativeId))]
  112. syncBatchCreative.run({ accountAdgroupMaps }).then(res => {
  113. res && getDynamicCreativeV3List.refresh()
  114. res ? message.success('同步成功!') : message.error('同步失败!')
  115. hide()
  116. }).catch(() => hide())
  117. } else {
  118. message.error('请勾选需要同步的创意')
  119. }
  120. }
  121. return <>
  122. <Form
  123. layout="inline"
  124. form={form}
  125. name="basignCreative"
  126. initialValues={queryForm}
  127. onFinish={onFinish}
  128. style={{ marginBottom: 6 }}
  129. >
  130. <Row gutter={[10, 10]}>
  131. <Col><Form.Item name='accountId' style={{ marginRight: 0 }}>
  132. <Input placeholder="广告账号,多个逗号隔开" style={{ width: 180 }} allowClear />
  133. </Form.Item></Col>
  134. <Col><Form.Item name='adgroupId' style={{ marginRight: 0 }}>
  135. <Input placeholder="广告ID" style={{ width: 120 }} allowClear />
  136. </Form.Item></Col>
  137. <Col><Form.Item name='creativeName' style={{ marginRight: 0 }}>
  138. <Input placeholder="创意名称" style={{ width: 120 }} allowClear />
  139. </Form.Item></Col>
  140. <Col><Form.Item name='creativeId' style={{ marginRight: 0 }}>
  141. <Input placeholder="创意ID" style={{ width: 120 }} allowClear />
  142. </Form.Item></Col>
  143. <Col><Form.Item name='configuredStatus' style={{ marginRight: 0 }}>
  144. <Select
  145. placeholder='启用禁用状态'
  146. style={{ minWidth: 120 }}
  147. showSearch
  148. allowClear
  149. filterOption={(input: any, option: any) =>
  150. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  151. }
  152. mode="multiple"
  153. >
  154. <Select.Option value={'AD_STATUS_NORMAL'}>有效</Select.Option>
  155. <Select.Option value={'AD_STATUS_SUSPEND'}>暂停</Select.Option>
  156. </Select>
  157. </Form.Item></Col>
  158. <Col><Form.Item name='deliveryMode' style={{ marginRight: 0 }}>
  159. <Select
  160. placeholder='投放模式'
  161. style={{ minWidth: 120 }}
  162. showSearch
  163. allowClear
  164. maxTagCount={1}
  165. filterOption={(input: any, option: any) =>
  166. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  167. }
  168. mode="multiple"
  169. >
  170. <Select.Option value={'DELIVERY_MODE_CUSTOMIZE'}>自定义创意</Select.Option>
  171. <Select.Option value={'DELIVERY_MODE_COMPONENT'}>组件化创意</Select.Option>
  172. </Select>
  173. </Form.Item></Col>
  174. <Col>
  175. <Form.Item name='date' style={{ marginRight: 0 }}>
  176. <DatePicker.RangePicker style={{ width: 260 }} placeholder={['创建开始日期', '创建结束日期']} />
  177. </Form.Item>
  178. </Col>
  179. <Col><Form.Item name='isDeleted' style={{ marginRight: 0 }}>
  180. <Select
  181. placeholder='是否删除?'
  182. style={{ minWidth: 100 }}
  183. showSearch
  184. allowClear
  185. filterOption={(input: any, option: any) =>
  186. (option!.children as unknown as string).toLowerCase().includes(input.toLowerCase())
  187. }
  188. mode="multiple"
  189. >
  190. <Select.Option value={true}>是</Select.Option>
  191. <Select.Option value={false}>否</Select.Option>
  192. </Select>
  193. </Form.Item></Col>
  194. <Col><Form.Item style={{ marginRight: 0 }}>
  195. <Space>
  196. <Button type="primary" htmlType="submit">搜索</Button>
  197. <Button onClick={() => {
  198. setQueryForm({ pageNum: 1, pageSize: queryForm.pageSize, userId })
  199. form.resetFields()
  200. }}>重置</Button>
  201. <Button disabled={selectedRows.length === 0} style={{ padding: 0 }} danger type="link" onClick={() => setSelectedRows([])}>清空已选({selectedRows.length})</Button>
  202. </Space>
  203. </Form.Item></Col>
  204. </Row>
  205. </Form>
  206. <TableData
  207. isCard={false}
  208. columns={() => tableConfig(reviewStatusDetails, (data, type) => dynamicHandle(type, data))}
  209. ajax={getDynamicCreativeV3List}
  210. fixed={{ left: 2, right: 4 }}
  211. dataSource={getDynamicCreativeV3List?.data?.data?.records}
  212. loading={getDynamicCreativeV3List?.loading || syncBatchCreative.loading || updateBatchDynamicCreativesInfo.loading}
  213. scroll={{ y: 560 }}
  214. syncAjax={sync}
  215. total={getDynamicCreativeV3List?.data?.data?.total}
  216. page={getDynamicCreativeV3List?.data?.data?.current}
  217. pageSize={getDynamicCreativeV3List?.data?.data?.size}
  218. myKey={'dynamicCreativeId'}
  219. gutter={[0, 10]}
  220. config={txDynamicConfig}
  221. configName="创意3.0"
  222. leftChild={<Space>
  223. <Select
  224. style={{ width: 120 }}
  225. onChange={(e) => {
  226. setHandleType(e)
  227. setSelectedRows([])
  228. }}
  229. value={handleType}
  230. dropdownMatchSelectWidth={false}
  231. options={[
  232. { label: '基本操作', value: 1 },
  233. { label: '复审操作', value: 2 },
  234. { label: '落地页替换', value: 3 },
  235. ]}
  236. />
  237. {handleType === 1 ? <>
  238. <Button type='primary' style={{ background: '#67c23a', borderColor: '#67c23a' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PlayCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('启动')}>启动</Button>
  239. <Button type='primary' style={{ background: '#e6a23c', borderColor: '#e6a23c' }} loading={updateBatchDynamicCreativesInfo.loading} icon={<PauseCircleOutlined />} disabled={selectedRows.length === 0} onClick={() => dynamicHandle('暂停')}>暂停</Button>
  240. <Popconfirm
  241. title="确定删除?"
  242. onConfirm={() => dynamicHandle('删除')}
  243. disabled={selectedRows.length === 0}
  244. >
  245. <Button type='primary' danger icon={<DeleteOutlined />} loading={delBatchCreative.loading} disabled={selectedRows.length === 0}>删除</Button>
  246. </Popconfirm>
  247. </> : handleType === 2 ? <>
  248. <RetriaModal
  249. selectedRows={selectedRows}
  250. onChange={() => setSelectedRows([])}
  251. />
  252. </> : <Button
  253. type='primary'
  254. disabled={selectedRows.length === 0}
  255. onClick={() => {
  256. if (selectedRows?.[0]?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === "WECHAT_CANVAS") { // 原生推广页
  257. setReplaceVisible(true)
  258. } else if (selectedRows?.[0]?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === "OFFICIAL") { // 官方落地页
  259. setReplaceGfVisible(true)
  260. } else {
  261. message.error('暂不支持该类型落地页替换')
  262. }
  263. }}
  264. >修改落地页</Button>}
  265. <Button type='dashed' onClick={() => { setLogVisible(true) }}>操作记录</Button>
  266. </Space>}
  267. rowSelection={{
  268. selectedRowKeys: selectedRows.map(item => item.dynamicCreativeId.toString()),
  269. getCheckboxProps: (record: any) => {
  270. if (handleType === 3) {
  271. if (selectedRows.length) {
  272. const { deliveryMode, creativeTemplateId, creativeComponents: { mainJumpInfo } } = selectedRows[0]
  273. const pageType: string | undefined = mainJumpInfo?.[0]?.value?.pageType
  274. const pageLength: number | undefined = mainJumpInfo?.length
  275. return {
  276. disabled: record.isDeleted ||
  277. record.deliveryMode !== deliveryMode ||
  278. record.creativeTemplateId !== creativeTemplateId ||
  279. record?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType !== pageType ||
  280. pageLength !== record?.creativeComponents?.mainJumpInfo?.length ||
  281. (mainJumpInfo?.[0]?.value?.pageType === "WECHAT_CANVAS" && record?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === "WECHAT_CANVAS" && record?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageSpec?.wechatCanvasSpec?.overrideCanvasHeadOption !== mainJumpInfo?.[0]?.value?.pageSpec?.wechatCanvasSpec?.overrideCanvasHeadOption)
  282. }
  283. } else {
  284. return { disabled: record.isDeleted }
  285. }
  286. }
  287. return { disabled: handleType === 2 ? record.isDeleted || record.reviewStatus !== 'AD_STATUS_DENIED' : record.isDeleted }
  288. },
  289. onSelect: (record: { dynamicCreativeId: number }, selected: boolean) => {
  290. if (selected) {
  291. selectedRows.push({ ...record })
  292. setSelectedRows([...selectedRows])
  293. } else {
  294. const newSelectAccData = selectedRows.filter((item: { dynamicCreativeId: number }) => item.dynamicCreativeId !== record.dynamicCreativeId)
  295. setSelectedRows([...newSelectAccData])
  296. }
  297. },
  298. onSelectAll: (selected: boolean, selectedRowss: { dynamicCreativeId: number }[], changeRows: { dynamicCreativeId: number, isDeleted: boolean }[]) => {
  299. if (selected) {
  300. const newSelectAccData = [...selectedRows]
  301. const firstData = selectedRows?.[0] || changeRows?.find((item: { isDeleted: boolean }) => !item.isDeleted)
  302. changeRows.forEach((item: any) => {
  303. const index = newSelectAccData.findIndex((ite: { dynamicCreativeId: number }) => ite.dynamicCreativeId === item.dynamicCreativeId)
  304. if (index === -1) {
  305. if (handleType === 3) {
  306. const isValid = !item.isDeleted &&
  307. item.deliveryMode === firstData.deliveryMode &&
  308. item.creativeTemplateId === firstData.creativeTemplateId &&
  309. item?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === firstData?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType &&
  310. firstData?.creativeComponents?.mainJumpInfo?.length === item?.creativeComponents?.mainJumpInfo?.length &&
  311. (firstData?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === "WECHAT_CANVAS" && item?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageType === "WECHAT_CANVAS" && item?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageSpec?.wechatCanvasSpec?.overrideCanvasHeadOption === firstData?.creativeComponents?.mainJumpInfo?.[0]?.value?.pageSpec?.wechatCanvasSpec?.overrideCanvasHeadOption);
  312. if (isValid) {
  313. newSelectAccData.push({ ...item });
  314. }
  315. } else {
  316. newSelectAccData.push({ ...item });
  317. }
  318. }
  319. })
  320. setSelectedRows([...newSelectAccData])
  321. } else {
  322. const newSelectAccData = selectedRows.filter((item: { dynamicCreativeId: number }) => {
  323. const index = changeRows.findIndex((ite: { dynamicCreativeId: number }) => ite.dynamicCreativeId === item.dynamicCreativeId)
  324. if (index !== -1) {
  325. return false
  326. } else {
  327. return true
  328. }
  329. })
  330. setSelectedRows([...newSelectAccData])
  331. }
  332. }
  333. }}
  334. onChange={(props: any) => {
  335. let { pagination } = props
  336. let { current, pageSize } = pagination
  337. setQueryForm({ ...queryForm, pageNum: current, pageSize })
  338. }}
  339. />
  340. {dynimicVisible && <ReviewDetails
  341. visible={dynimicVisible}
  342. dynamic={dynimicData}
  343. onClose={() => {
  344. setDynamicData({})
  345. setDynamicVisible(false)
  346. }}
  347. />}
  348. {failVisible && <Modal
  349. title={<strong>报错信息</strong>}
  350. open={failVisible}
  351. className='modalResetCss'
  352. width={650}
  353. onCancel={() => { setFailVisible(false); setFailIdList([]) }}
  354. footer={null}
  355. >
  356. <Table
  357. size="small"
  358. bordered
  359. rowKey={'creativeId'}
  360. columns={[{
  361. title: '创意ID',
  362. dataIndex: 'creativeId',
  363. key: 'creativeId',
  364. width: 110,
  365. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  366. }, {
  367. title: 'code',
  368. dataIndex: 'code',
  369. key: 'code',
  370. width: 70,
  371. align: 'center',
  372. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  373. }, {
  374. title: '错误信息',
  375. dataIndex: 'messageCn',
  376. key: 'messageCn',
  377. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  378. }]}
  379. dataSource={failIdList}
  380. />
  381. </Modal>}
  382. {logVisible && <HandleLog
  383. visible={logVisible}
  384. onClose={() => {
  385. setLogVisible(false)
  386. }}
  387. userId={userId}
  388. />}
  389. {/* 修改原生落地页 */}
  390. {replaceVisible && <ReplacePage
  391. selectedRows={selectedRows}
  392. visible={replaceVisible}
  393. onClose={() => {
  394. setReplaceVisible(false)
  395. }}
  396. onChange={() => {
  397. setReplaceVisible(false)
  398. setSelectedRows([])
  399. }}
  400. />}
  401. {/* 修改官方落地页 */}
  402. {replaceGfVisible && <ReplaceGfPage
  403. selectedRows={selectedRows}
  404. visible={replaceGfVisible}
  405. onClose={() => {
  406. setReplaceGfVisible(false)
  407. }}
  408. onChange={() => {
  409. setReplaceGfVisible(false)
  410. setSelectedRows([])
  411. }}
  412. />}
  413. </>
  414. }
  415. export default React.memo(Creative)