updateAd3.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. import { useAjax } from "@/Hook/useAjax"
  2. import { delBatchApi, modifyAdTimeBatchApi, modifyAmountBatchApi, modifyDailyBudgetBatchApi, updateBatchAdgroupInfoApi } from "@/services/launchAdq/adqv3"
  3. import { Button, Card, DatePicker, Form, Input, InputNumber, Modal, Radio, Select, Space, Table, message } from "antd"
  4. import React, { useEffect, useState } from "react"
  5. import '../../tencentAdPutIn/index.less'
  6. import { RangePickerProps } from "antd/lib/date-picker"
  7. import moment from "moment"
  8. import style from '../../tencentAdPutIn/create/index.less'
  9. import TimeInSelect from "@/pages/launchSystemNew/components/timeInSelect"
  10. import { getTimeSeriesList } from "@/pages/launchSystemNew/adq/ad/const"
  11. import { txtLength } from "@/utils/utils"
  12. import InputName from "@/components/InputName"
  13. import New1Radio from "../../components/New1Radio"
  14. import { SelectTimeList } from "../../tencentAdPutIn/const"
  15. interface Props {
  16. type: '修改出价' | '修改名称' | '修改日限额' | '修改投放时间' | '删除' | '深度优化ROI' | '修改投放首日开始时间'
  17. updateData: any[],
  18. visible?: boolean,
  19. onClose?: () => void
  20. onChange?: () => void
  21. }
  22. const UpdateAd3: React.FC<Props> = ({ visible, type, onClose, onChange, updateData }) => {
  23. /****************************************/
  24. const [form] = Form.useForm();
  25. const timeSeriesType = Form.useWatch('timeSeriesType', form)
  26. const updateType = Form.useWatch('updateType', form)
  27. const [failIdList, setFailIdList] = useState<{ adgroupId: number, code: number, message: string, messageCn: string }[]>([])
  28. const [failVisible, setFailVisible] = useState<boolean>(false)
  29. const modifyAmountBatch = useAjax((params) => modifyAmountBatchApi(params)) // 出价
  30. const modifyAdTimeBatch = useAjax((params) => modifyAdTimeBatchApi(params)) // 时间
  31. const updateBatchAdgroupInfo = useAjax((params) => updateBatchAdgroupInfoApi(params)) // 名称
  32. const modifyDailyBudgetBatch = useAjax((params) => modifyDailyBudgetBatchApi(params)) // 日限额
  33. const delBatch = useAjax((params) => delBatchApi(params)) // 删除
  34. /****************************************/
  35. useEffect(() => {
  36. if (type === '删除') {
  37. form.setFieldsValue({ delAd: updateData.map(item => item.adgroupName) })
  38. } else if (type === '深度优化ROI') {
  39. form.setFieldsValue({ deepConversionSpec: updateData?.[0]?.deepConversionSpec })
  40. }
  41. }, [type, updateData])
  42. const handleOk = (values: any) => {
  43. console.log(values)
  44. let accountAdgroupMaps = [...new Set(updateData?.map(item => item.accountId + ',' + item.adgroupId))]
  45. switch (type) {
  46. case '修改出价':
  47. let paramsCj: any = {}
  48. if (values?.updateType === 'percent') {
  49. paramsCj.bidAmountPercent = values?.bidAmountPercent / 100
  50. } else {
  51. paramsCj.bidAmount = values?.bidAmount
  52. }
  53. modifyAmountBatch.run({ accountAdgroupMaps, ...paramsCj }).then(res => {
  54. if (res?.failIdList?.length === 0) {
  55. message.success(`修改操作完成!`)
  56. onChange?.()
  57. } else {
  58. setFailIdList(res?.list || [])
  59. setFailVisible(true)
  60. }
  61. })
  62. break
  63. case '修改名称':
  64. case '深度优化ROI':
  65. case '修改投放首日开始时间':
  66. let params2: any = {}
  67. if (type === '深度优化ROI') {
  68. params2 = JSON.parse(JSON.stringify(values))
  69. if (params2.updateType === 'percent') {
  70. delete params2?.deepConversionSpec?.deepConversionWorthSpec?.expectedRoi
  71. params2.deepConversionSpec.deepConversionWorthSpec.expectedRoiPercent = params2?.deepConversionSpec?.deepConversionWorthSpec?.expectedRoiPercent / 100
  72. } else {
  73. delete params2?.deepConversionSpec?.deepConversionWorthSpec?.expectedRoiPercent
  74. }
  75. delete params2.updateType
  76. } else {
  77. params2 = values
  78. }
  79. updateBatchAdgroupInfo.run({ accountAdgroupMaps, ...params2 }).then(res => {
  80. if (res?.failIdList?.length === 0) {
  81. message.success(`修改操作完成!`)
  82. onChange?.()
  83. } else {
  84. setFailIdList(res?.list || [])
  85. setFailVisible(true)
  86. }
  87. })
  88. break
  89. case '修改投放时间':
  90. let params = { accountAdgroupMaps, ...values }
  91. params.beginDate = moment(params.date[0]).format('YYYY-MM-DD')
  92. params.endDate = moment(params.date[1]).format('YYYY-MM-DD')
  93. if (params.timeSeriesType === '0') {
  94. // params.timeSeries = Array(336).fill(1).join('');
  95. } else {
  96. params.timeSeries = params.timeSeries.join('');
  97. }
  98. delete params.timeSeriesType
  99. delete params.date
  100. modifyAdTimeBatch.run(params).then(res => {
  101. if (res?.failIdList?.length === 0) {
  102. message.success(`修改操作完成!`)
  103. onChange?.()
  104. } else {
  105. setFailIdList(res?.list || [])
  106. setFailVisible(true)
  107. }
  108. })
  109. break
  110. case '修改日限额':
  111. modifyDailyBudgetBatch.run({ accountAdgroupMaps, ...values }).then(res => {
  112. if (res?.failIdList?.length === 0) {
  113. message.success(`修改操作完成!`)
  114. onChange?.()
  115. } else {
  116. setFailIdList(res?.list || [])
  117. setFailVisible(true)
  118. }
  119. })
  120. break
  121. case '删除':
  122. delBatch.run({ accountAdgroupMaps }).then(res => {
  123. if (res?.failIdList?.length === 0) {
  124. message.success(`删除广告操作完成!`)
  125. onChange?.()
  126. } else {
  127. setFailIdList(res?.list || [])
  128. setFailVisible(true)
  129. }
  130. })
  131. break
  132. }
  133. }
  134. /** 禁止选择以前时间 */
  135. const disabledDate: RangePickerProps['disabledDate'] = current => {
  136. // Can not select days before today and today
  137. return current && current < moment().startOf('day');
  138. };
  139. return <>
  140. <Modal
  141. title={<strong>{type}</strong>}
  142. open={visible}
  143. footer={null}
  144. onCancel={onClose}
  145. bodyStyle={{ padding: '0 0 40px', position: 'relative', borderRadius: '0 0 8px 8px' }}
  146. className='modalResetCss'
  147. width={type === '修改投放时间' ? 900 : type === '深度优化ROI' ? 800 : 600}
  148. >
  149. <Form
  150. form={form}
  151. name="updateAd3.0"
  152. labelAlign='left'
  153. labelCol={{ span: ['修改投放首日开始时间', '修改出价'].includes(type) ? 6 : 4 }}
  154. colon={false}
  155. style={{ backgroundColor: '#f1f4fc', maxHeight: 600, overflow: 'hidden', overflowY: 'auto', padding: '10px 10px 10px', borderRadius: '0 0 8px 8px' }}
  156. scrollToFirstError
  157. onFinishFailed={({ errorFields }) => {
  158. message.error(errorFields?.[0]?.errors?.[0])
  159. }}
  160. onFinish={handleOk}
  161. initialValues={{ timeSeriesType: '0', timeSeries: getTimeSeriesList(), updateType: 'fixed' }}
  162. >
  163. <Card
  164. title={<strong style={{ fontSize: 14 }}>{type === '删除' ? '确认删除?' : '修改设置'}</strong>}
  165. className="cardResetCss"
  166. >
  167. {type === '修改出价' ? <>
  168. <Form.Item
  169. label={<strong>出价修改方式</strong>}
  170. name='updateType'
  171. rules={[
  172. { required: true, message: '请选择出价修改方式' }
  173. ]}
  174. >
  175. <Radio.Group buttonStyle="solid">
  176. <Radio.Button value="fixed">固定值</Radio.Button>
  177. <Radio.Button value="percent">百分比上下浮动修改</Radio.Button>
  178. </Radio.Group>
  179. </Form.Item>
  180. {updateType === 'fixed' ? <Form.Item
  181. label={<strong>出价</strong>}
  182. name='bidAmount'
  183. rules={[
  184. { required: true, message: '请输入' }
  185. ]}
  186. >
  187. <InputNumber min={0} style={{ width: '100%' }} placeholder="请输入价格 元" />
  188. </Form.Item> : <Form.Item
  189. label={<strong>浮动比</strong>}
  190. name='bidAmountPercent'
  191. rules={[
  192. { required: true, message: '请输入' }
  193. ]}
  194. >
  195. <InputNumber placeholder="出价,原有基础上下调百分比" style={{ width: '100%' }} addonAfter="%" />
  196. </Form.Item>}
  197. </> : type === '修改名称' ? <Form.Item
  198. label={<strong>广告名称</strong>}
  199. name='adgroupName'
  200. // tooltip="下标、日期时分秒、广告账户创建时默认自带"
  201. rules={[
  202. { required: true, message: '请输入广告名称!' },
  203. {
  204. required: true, message: '广告名称不能包含特殊字符:< > & ‘ ” / 以及TAB、换行、回车键,请修改', validator(_, value) {
  205. let reg = /[&‘’“”/\n\t\f]/ig
  206. if (value && reg.test(value)) {
  207. return Promise.reject()
  208. }
  209. return Promise.resolve()
  210. }
  211. },
  212. {
  213. required: true, message: '请确保广告名称长度不超过60个字(1个汉字等于2个字符)', validator(_, value) {
  214. if (value && txtLength(value) > 50) {
  215. return Promise.reject()
  216. }
  217. return Promise.resolve()
  218. }
  219. }
  220. ]}
  221. >
  222. <InputName placeholder='广告名称' style={{ width: 420 }} length={50} />
  223. </Form.Item> : type === '修改日限额' ? <Form.Item
  224. label={<strong>日限额</strong>}
  225. name='bidAmount'
  226. rules={[
  227. { required: true, message: '请输入' }
  228. ]}
  229. >
  230. <InputNumber min={0} style={{ width: '100%' }} placeholder="请输入日限额 元" />
  231. </Form.Item> : type === '修改投放时间' ? <>
  232. <Form.Item label={<strong>投放日期</strong>} name='date' rules={[{ required: true, message: '请选择投放日期' }]}>
  233. <DatePicker.RangePicker disabledDate={disabledDate} />
  234. </Form.Item>
  235. <Form.Item label={<strong>投放时间</strong>}>
  236. <Card bordered className="cardResetCss newCss" bodyStyle={{ padding: 0 }}>
  237. <div className={style.newSpace}>
  238. <div className={style.newSpace_top}>
  239. <div className={style.newSpace_title} style={{ width: 78 }}>选择时段</div>
  240. <Form.Item name='timeSeriesType' style={{ marginBottom: 0 }}>
  241. <Radio.Group>
  242. <Radio value="0">全天</Radio>
  243. <Radio value="2">指定多个时段</Radio>
  244. </Radio.Group>
  245. </Form.Item>
  246. </div>
  247. {timeSeriesType === '2' && <div className={style.newSpace_bottom} style={{ marginLeft: 65 }}>
  248. <Form.Item name='timeSeries' noStyle rules={[{ required: true, message: '请选择时段' }]}>
  249. <TimeInSelect />
  250. </Form.Item>
  251. </div>}
  252. </div>
  253. </Card>
  254. </Form.Item>
  255. </> : type === '删除' ? <Form.Item label={<strong>广告名称</strong>} name='delAd' rules={[{ required: true, message: '请选择广告' }]}>
  256. <Input.TextArea disabled />
  257. </Form.Item> : type === '深度优化ROI' ? <>
  258. <Form.Item label={<strong>深度优化类型</strong>} name={['deepConversionSpec', 'deepConversionType']} rules={[{ required: true, message: '请选择深度优化类型' }]}>
  259. <New1Radio data={[{ label: '优化ROI', value: 'DEEP_CONVERSION_WORTH', disabled: true }]} />
  260. </Form.Item>
  261. <Form.Item label={<strong>深度优化目标</strong>} name={['deepConversionSpec', 'deepConversionWorthSpec', 'goal']} rules={[{ required: true, message: '请选择深度优化目标' }]}>
  262. <Select style={{ width: 480 }} placeholder='请选择'>
  263. <Select.Option value={'GOAL_1DAY_PURCHASE_ROAS'}>首日付费ROI</Select.Option>
  264. </Select>
  265. </Form.Item>
  266. <Form.Item
  267. label={<strong>期望ROI修改方式</strong>}
  268. name='updateType'
  269. rules={[
  270. { required: true, message: '请选择期望ROI修改方式' }
  271. ]}
  272. >
  273. <Radio.Group buttonStyle="solid">
  274. <Radio.Button value="fixed">固定值</Radio.Button>
  275. <Radio.Button value="percent">百分比上下浮动修改</Radio.Button>
  276. </Radio.Group>
  277. </Form.Item>
  278. {updateType === 'fixed' ? <Form.Item
  279. label={<strong>期望ROI</strong>}
  280. name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoi']}
  281. rules={[
  282. { required: true, message: '请输入期望ROI' },
  283. { type: 'number', min: 0.001, max: 1000, message: '范围0.001~1000' },
  284. {
  285. validator: (_: any, value: string) => {
  286. if (!value || /^\d+(\.\d{0,3})?$/.test(value)) {
  287. return Promise.resolve();
  288. }
  289. return Promise.reject(new Error('请输入最多三位小数'));
  290. }
  291. }
  292. ]}
  293. >
  294. <InputNumber style={{ width: 480 }} placeholder={`期望ROI目标范围0.001~1000,输入0.05,表示ROI目标为5%`} />
  295. </Form.Item> : <Form.Item
  296. label={<strong>期望ROI浮动比</strong>}
  297. name={['deepConversionSpec', 'deepConversionWorthSpec', 'expectedRoiPercent']}
  298. rules={[{ required: true, message: '请输入期望ROI浮动比' }]}
  299. >
  300. <InputNumber style={{ width: 480 }} placeholder="期望ROI,原有基础上下调百分比" addonAfter="%" />
  301. </Form.Item>}
  302. </> : type === '修改投放首日开始时间' ? <Form.Item name='firstDayBeginTime' label={<strong>首日开始时间</strong>} rules={[{ required: true, message: '请选择首日开始时间' }]}>
  303. <Select
  304. style={{ width: 180 }}
  305. allowClear
  306. placeholder='请选择首日开始时间'
  307. options={SelectTimeList}
  308. />
  309. </Form.Item> : null}
  310. </Card>
  311. <Form.Item className="submit_pull">
  312. <Space>
  313. <Button onClick={onClose}>取消</Button>
  314. <Button type="primary" htmlType="submit" className="modalResetCss" loading={modifyAmountBatch.loading || modifyAdTimeBatch.loading || updateBatchAdgroupInfo.loading || modifyDailyBudgetBatch.loading || delBatch.loading}>
  315. 确定
  316. </Button>
  317. </Space>
  318. </Form.Item>
  319. </Form>
  320. </Modal>
  321. {failVisible && <Modal
  322. title={<strong>报错信息</strong>}
  323. open={failVisible}
  324. className='modalResetCss'
  325. width={650}
  326. onCancel={() => { setFailVisible(false); setFailIdList([]) }}
  327. footer={null}
  328. >
  329. <Table
  330. size="small"
  331. bordered
  332. rowKey={'adgroupId'}
  333. columns={[{
  334. title: '广告ID',
  335. dataIndex: 'adgroupId',
  336. key: 'adgroupId',
  337. width: 110,
  338. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  339. }, {
  340. title: 'code',
  341. dataIndex: 'code',
  342. key: 'code',
  343. width: 70,
  344. align: 'center',
  345. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  346. }, {
  347. title: '错误信息',
  348. dataIndex: 'messageCn',
  349. key: 'messageCn',
  350. render: (value) => <span style={{ fontSize: 12 }}>{value}</span>,
  351. }]}
  352. dataSource={failIdList}
  353. />
  354. </Modal>}
  355. </>
  356. }
  357. export default React.memo(UpdateAd3)